]> pilppa.com Git - linux-2.6-omap-h63xx.git/commitdiff
Staging: Add the Meilhaus ME-IDS driver package
authorDavid Kiliani <mail@davidkiliani.de>
Fri, 31 Oct 2008 23:39:12 +0000 (00:39 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 6 Jan 2009 21:52:29 +0000 (13:52 -0800)
Originally written by Guenter Gebhardt <g.gebhardt@meilhaus.de>
and Krzysztof Gantzke <k.gantzke@meilhaus.de>

This is the drv/lnx/mod directory of ME-IDS 1.2.9 tarball with
some files from drv/lnx/include.

Signed-off-by: David Kiliani <mail@davidkiliani.de>
Cc: Guenter Gebhardt <g.gebhardt@meilhaus.de>
Cc: Krzysztof Gantzke <k.gantzke@meilhaus.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
133 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/meilhaus/Kconfig [new file with mode: 0644]
drivers/staging/meilhaus/Makefile [new file with mode: 0644]
drivers/staging/meilhaus/TODO [new file with mode: 0644]
drivers/staging/meilhaus/me0600_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me0600_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_dio.c [new file with mode: 0644]
drivers/staging/meilhaus/me0600_dio.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_dio_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_ext_irq.c [new file with mode: 0644]
drivers/staging/meilhaus/me0600_ext_irq.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_ext_irq_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_optoi.c [new file with mode: 0644]
drivers/staging/meilhaus/me0600_optoi.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_optoi_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_relay.c [new file with mode: 0644]
drivers/staging/meilhaus/me0600_relay.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_relay_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_ttli.c [new file with mode: 0644]
drivers/staging/meilhaus/me0600_ttli.h [new file with mode: 0644]
drivers/staging/meilhaus/me0600_ttli_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me0900_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me0900_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me0900_di.c [new file with mode: 0644]
drivers/staging/meilhaus/me0900_di.h [new file with mode: 0644]
drivers/staging/meilhaus/me0900_do.c [new file with mode: 0644]
drivers/staging/meilhaus/me0900_do.h [new file with mode: 0644]
drivers/staging/meilhaus/me0900_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me1000_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me1000_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me1000_dio.c [new file with mode: 0644]
drivers/staging/meilhaus/me1000_dio.h [new file with mode: 0644]
drivers/staging/meilhaus/me1000_dio_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me1400_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me1400_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me1400_ext_irq.c [new file with mode: 0644]
drivers/staging/meilhaus/me1400_ext_irq.h [new file with mode: 0644]
drivers/staging/meilhaus/me1400_ext_irq_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me1600_ao.c [new file with mode: 0644]
drivers/staging/meilhaus/me1600_ao.h [new file with mode: 0644]
drivers/staging/meilhaus/me1600_ao_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me1600_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me1600_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ai.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ai.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ai_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ao.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ao.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ao_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_di.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_di.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_dio.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_dio.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_dio_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_do.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_do.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ext_irq.c [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ext_irq.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_ext_irq_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me4600_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me6000_ao.c [new file with mode: 0644]
drivers/staging/meilhaus/me6000_ao.h [new file with mode: 0644]
drivers/staging/meilhaus/me6000_ao_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me6000_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me6000_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me6000_dio.c [new file with mode: 0644]
drivers/staging/meilhaus/me6000_dio.h [new file with mode: 0644]
drivers/staging/meilhaus/me6000_dio_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me6000_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8100_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me8100_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me8100_di.c [new file with mode: 0644]
drivers/staging/meilhaus/me8100_di.h [new file with mode: 0644]
drivers/staging/meilhaus/me8100_di_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8100_do.c [new file with mode: 0644]
drivers/staging/meilhaus/me8100_do.h [new file with mode: 0644]
drivers/staging/meilhaus/me8100_do_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8100_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_device.c [new file with mode: 0644]
drivers/staging/meilhaus/me8200_device.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_di.c [new file with mode: 0644]
drivers/staging/meilhaus/me8200_di.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_di_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_dio.c [new file with mode: 0644]
drivers/staging/meilhaus/me8200_dio.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_dio_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_do.c [new file with mode: 0644]
drivers/staging/meilhaus/me8200_do.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_do_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8200_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8254.c [new file with mode: 0644]
drivers/staging/meilhaus/me8254.h [new file with mode: 0644]
drivers/staging/meilhaus/me8254_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/me8255.c [new file with mode: 0644]
drivers/staging/meilhaus/me8255.h [new file with mode: 0644]
drivers/staging/meilhaus/me8255_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/mecirc_buf.h [new file with mode: 0644]
drivers/staging/meilhaus/mecommon.h [new file with mode: 0644]
drivers/staging/meilhaus/medebug.h [new file with mode: 0644]
drivers/staging/meilhaus/medefines.h [new file with mode: 0644]
drivers/staging/meilhaus/medevice.c [new file with mode: 0644]
drivers/staging/meilhaus/medevice.h [new file with mode: 0644]
drivers/staging/meilhaus/medlist.c [new file with mode: 0644]
drivers/staging/meilhaus/medlist.h [new file with mode: 0644]
drivers/staging/meilhaus/medlock.c [new file with mode: 0644]
drivers/staging/meilhaus/medlock.h [new file with mode: 0644]
drivers/staging/meilhaus/medriver.h [new file with mode: 0644]
drivers/staging/meilhaus/medummy.c [new file with mode: 0644]
drivers/staging/meilhaus/medummy.h [new file with mode: 0644]
drivers/staging/meilhaus/meerror.h [new file with mode: 0644]
drivers/staging/meilhaus/mefirmware.c [new file with mode: 0644]
drivers/staging/meilhaus/mefirmware.h [new file with mode: 0644]
drivers/staging/meilhaus/meids.h [new file with mode: 0644]
drivers/staging/meilhaus/meinternal.h [new file with mode: 0644]
drivers/staging/meilhaus/meioctl.h [new file with mode: 0644]
drivers/staging/meilhaus/memain.c [new file with mode: 0644]
drivers/staging/meilhaus/memain.h [new file with mode: 0644]
drivers/staging/meilhaus/meplx_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/meslist.c [new file with mode: 0644]
drivers/staging/meilhaus/meslist.h [new file with mode: 0644]
drivers/staging/meilhaus/meslock.c [new file with mode: 0644]
drivers/staging/meilhaus/meslock.h [new file with mode: 0644]
drivers/staging/meilhaus/mesubdevice.c [new file with mode: 0644]
drivers/staging/meilhaus/mesubdevice.h [new file with mode: 0644]
drivers/staging/meilhaus/metempl_device.c [new file with mode: 0644]
drivers/staging/meilhaus/metempl_device.h [new file with mode: 0644]
drivers/staging/meilhaus/metempl_sub.c [new file with mode: 0644]
drivers/staging/meilhaus/metempl_sub.h [new file with mode: 0644]
drivers/staging/meilhaus/metempl_sub_reg.h [new file with mode: 0644]
drivers/staging/meilhaus/metypes.h [new file with mode: 0644]

index d8f26acf957b1836b43df96e8ef943781968cf15..a3e361db69fe69e4d8d5fea11b4c00ef8a5ba867 100644 (file)
@@ -49,6 +49,8 @@ source "drivers/staging/sxg/Kconfig"
 
 source "drivers/staging/me4000/Kconfig"
 
+source "drivers/staging/meilhaus/Kconfig"
+
 source "drivers/staging/go7007/Kconfig"
 
 source "drivers/staging/usbip/Kconfig"
index dd9a625d46b98e481787224490fa12ad76239f4c..06613bb05085f8fb47b4be128db0e49fabb650e0 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_ET131X)            += et131x/
 obj-$(CONFIG_SLICOSS)          += slicoss/
 obj-$(CONFIG_SXG)              += sxg/
 obj-$(CONFIG_ME4000)           += me4000/
+obj-$(CONFIG_MEILHAUS)         += meilhaus/
 obj-$(CONFIG_VIDEO_GO7007)     += go7007/
 obj-$(CONFIG_USB_IP_COMMON)    += usbip/
 obj-$(CONFIG_W35UND)           += winbond/
diff --git a/drivers/staging/meilhaus/Kconfig b/drivers/staging/meilhaus/Kconfig
new file mode 100644 (file)
index 0000000..6def83f
--- /dev/null
@@ -0,0 +1,127 @@
+#
+# Meilhaus configuration
+#
+
+menuconfig MEILHAUS
+       tristate "Meilhaus support"
+       ---help---
+         If you have a Meilhaus card, say Y (or M) here.
+
+         You need both this driver, and the driver for the particular
+         data collection card.
+
+         To compile this driver as a module, choose M here. The module will
+         be called memain.
+
+if MEILHAUS
+
+config ME0600
+       tristate "Meilhaus ME-600 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-600 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me0600.
+
+config ME0900
+       tristate "Meilhaus ME-900 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-900 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me0900.
+
+config ME1000
+       tristate "Meilhaus ME-1000 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-1000 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me1000.
+
+config ME1400
+       tristate "Meilhaus ME-1400 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-1400 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me1400.
+
+config ME1600
+       tristate "Meilhaus ME-1600 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-1600 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me1600.
+
+config ME4600
+       tristate "Meilhaus ME-4600 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-4600 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me4600.
+
+config ME6000
+       tristate "Meilhaus ME-6000 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-6000 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me6000.
+
+config ME8100
+       tristate "Meilhaus ME-8100 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-8100 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me8100.
+
+config ME8200
+       tristate "Meilhaus ME-8200 support"
+       default n
+       depends on PCI
+       help
+         This driver supports the Meilhaus ME-8200 family of boards
+         that do data collection and multipurpose I/O.
+
+         To compile this driver as a module, choose M here: the module
+         will be called me8200.
+
+config MEDUMMY
+       tristate "Meilhaus dummy driver"
+       default n
+       depends on PCI
+       help
+         This provides a dummy driver for the Meilhaus driver package
+
+         To compile this driver as a module, choose M here: the module
+         will be called medummy.
+
+endif # MEILHAUS
diff --git a/drivers/staging/meilhaus/Makefile b/drivers/staging/meilhaus/Makefile
new file mode 100644 (file)
index 0000000..5ab2c1c
--- /dev/null
@@ -0,0 +1,43 @@
+#
+# Makefile for Meilhaus linux driver system
+#
+
+obj-$(CONFIG_MEILHAUS) += memain.o
+obj-$(CONFIG_ME1600) += me1600.o
+obj-$(CONFIG_ME1000) += me1000.o
+obj-$(CONFIG_ME1400) += me1400.o
+obj-$(CONFIG_ME4600) += me4600.o
+obj-$(CONFIG_ME6000) += me6000.o
+obj-$(CONFIG_ME0600) += me0600.o
+obj-$(CONFIG_ME8100) += me8100.o
+obj-$(CONFIG_ME8200) += me8200.o
+obj-$(CONFIG_ME0900) += me0900.o
+obj-$(CONFIG_MEDUMMY) += medummy.o
+
+
+me1600-objs := medevice.o medlist.o medlock.o me1600_device.o
+me1600-objs += mesubdevice.o meslist.o meslock.o me1600_ao.o
+
+me1000-objs := medevice.o medlist.o medlock.o me1000_device.o
+me1000-objs += mesubdevice.o meslist.o meslock.o me1000_dio.o
+
+me1400-objs := medevice.o medlist.o medlock.o me1400_device.o
+me1400-objs += mesubdevice.o meslist.o meslock.o me8254.o me8255.o me1400_ext_irq.o
+
+me4600-objs := medevice.o medlist.o medlock.o mefirmware.o me4600_device.o
+me4600-objs += mesubdevice.o meslist.o meslock.o me4600_do.o me4600_di.o me4600_dio.o me8254.o me4600_ai.o me4600_ao.o me4600_ext_irq.o
+
+me6000-objs := medevice.o medlist.o medlock.o mefirmware.o me6000_device.o
+me6000-objs += mesubdevice.o meslist.o meslock.o me6000_dio.o me6000_ao.o
+
+me0600-objs := medevice.o medlist.o medlock.o me0600_device.o
+me0600-objs += mesubdevice.o meslist.o meslock.o me0600_relay.o me0600_ttli.o me0600_optoi.o me0600_dio.o me0600_ext_irq.o
+
+me8100-objs := medevice.o medlist.o medlock.o me8100_device.o
+me8100-objs += mesubdevice.o meslist.o meslock.o me8100_di.o me8100_do.o me8254.o
+
+me8200-objs := medevice.o medlist.o medlock.o me8200_device.o
+me8200-objs += mesubdevice.o meslist.o meslock.o me8200_di.o me8200_do.o me8200_dio.o
+
+me0900-objs := medevice.o medlist.o medlock.o me0900_device.o
+me0900-objs += mesubdevice.o meslist.o meslock.o me0900_do.o me0900_di.o
diff --git a/drivers/staging/meilhaus/TODO b/drivers/staging/meilhaus/TODO
new file mode 100644 (file)
index 0000000..6ec2520
--- /dev/null
@@ -0,0 +1,10 @@
+TODO:
+       - checkpatch.pl cleanups
+       - sparse issues
+       - Lindent
+       - audit userspace interface
+       - handle firmware properly
+       - possible comedi merge
+
+Please send cleanup patches to Greg Kroah-Hartman <greg@kroah.com>
+and CC: David Kiliani <mail@davidkiliani.de>
diff --git a/drivers/staging/meilhaus/me0600_device.c b/drivers/staging/meilhaus/me0600_device.c
new file mode 100644 (file)
index 0000000..8950e47
--- /dev/null
@@ -0,0 +1,215 @@
+/**
+ * @file me0600_device.c
+ *
+ * @brief ME-630 device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "me0600_device.h"
+#include "mesubdevice.h"
+#include "me0600_relay.h"
+#include "me0600_ttli.h"
+#include "me0600_optoi.h"
+#include "me0600_dio.h"
+#include "me0600_ext_irq.h"
+
+me_device_t *me0600_pci_constructor(struct pci_dev *pci_device)
+{
+       me0600_device_t *me0600_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me0600_device = kmalloc(sizeof(me0600_device_t), GFP_KERNEL);
+
+       if (!me0600_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(me0600_device, 0, sizeof(me0600_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me0600_device, pci_device);
+
+       if (err) {
+               kfree(me0600_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           me0600_versions_get_device_index(me0600_device->base.info.pci.
+                                            device_id);
+
+       // Initialize spin lock .
+       spin_lock_init(&me0600_device->dio_ctrl_reg_lock);
+       spin_lock_init(&me0600_device->intcsr_lock);
+
+       // Create subdevice instances.
+
+       for (i = 0; i < me0600_versions[version_idx].optoi_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me0600_optoi_constructor(me0600_device->
+                                                               base.info.pci.
+                                                               reg_bases[2]);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0600_device);
+                       kfree(me0600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0600_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me0600_versions[version_idx].relay_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me0600_relay_constructor(me0600_device->
+                                                               base.info.pci.
+                                                               reg_bases[2]);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0600_device);
+                       kfree(me0600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0600_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me0600_versions[version_idx].ttli_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me0600_ttli_constructor(me0600_device->
+                                                              base.info.pci.
+                                                              reg_bases[2]);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0600_device);
+                       kfree(me0600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0600_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me0600_versions[version_idx].dio_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me0600_dio_constructor(me0600_device->
+                                                             base.info.pci.
+                                                             reg_bases[2], i,
+                                                             &me0600_device->
+                                                             dio_ctrl_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0600_device);
+                       kfree(me0600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0600_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me0600_versions[version_idx].ext_irq_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *)
+                   me0600_ext_irq_constructor(me0600_device->base.info.pci.
+                                              reg_bases[1],
+                                              me0600_device->base.info.pci.
+                                              reg_bases[2],
+                                              &me0600_device->intcsr_lock, i,
+                                              me0600_device->base.irq);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0600_device);
+                       kfree(me0600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0600_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me0600_device;
+}
+
+// Init and exit of module.
+
+static int __init me0600_init(void)
+{
+       PDEBUG("executed.\n");
+       return 0;
+}
+
+static void __exit me0600_exit(void)
+{
+       PDEBUG("executed.\n");
+}
+
+module_init(me0600_init);
+
+module_exit(me0600_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for ME-6xx Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-6xx Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me0600_pci_constructor);
diff --git a/drivers/staging/meilhaus/me0600_device.h b/drivers/staging/meilhaus/me0600_device.h
new file mode 100644 (file)
index 0000000..d93a8ae
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * @file me0600_device.h
+ *
+ * @brief ME-630 device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_DEVICE_H
+#define _ME0600_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding ME-630 device capabilities.
+ */
+typedef struct me0600_version {
+       uint16_t device_id;
+       unsigned int relay_subdevices;
+       unsigned int ttli_subdevices;
+       unsigned int optoi_subdevices;
+       unsigned int dio_subdevices;
+       unsigned int ext_irq_subdevices;
+} me0600_version_t;
+
+/**
+ * @brief Device capabilities.
+ */
+static me0600_version_t me0600_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME0630, 1, 1, 1, 2, 2},
+       {0},
+};
+
+#define ME0600_DEVICE_VERSIONS (sizeof(me0600_versions) / sizeof(me0600_version_t) - 1)        /**< Returns the number of entries in #me0600_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me0600_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me0600_versions.
+ */
+static inline unsigned int me0600_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME0600_DEVICE_VERSIONS; i++)
+               if (me0600_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-630 device class structure.
+ */
+typedef struct me0600_device {
+       me_device_t base;                       /**< The Meilhaus device base class. */
+
+       /* Child class attributes. */
+       spinlock_t dio_ctrl_reg_lock;
+       spinlock_t intcsr_lock;
+} me0600_device_t;
+
+/**
+ * @brief The ME-630 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-630 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me0600_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_dio.c b/drivers/staging/meilhaus/me0600_dio.c
new file mode 100644 (file)
index 0000000..3a27757
--- /dev/null
@@ -0,0 +1,415 @@
+/**
+ * @file me0600_dio.c
+ *
+ * @brief ME-630 digital input/output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me0600_dio_reg.h"
+#include "me0600_dio.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me0600_dio_io_reset_subdevice(struct me_subdevice *subdevice,
+                                        struct file *filep, int flags)
+{
+       me0600_dio_subdevice_t *instance;
+       uint8_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_dio_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inb(instance->ctrl_reg);
+       mode &= ~(0x3 << (instance->dio_idx * 2));
+       outb(mode, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, mode);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outb(0x00, instance->port_reg);
+       PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0x00);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_dio_io_single_config(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int single_config,
+                                      int ref,
+                                      int trig_chan,
+                                      int trig_type, int trig_edge, int flags)
+{
+       me0600_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+       int size =
+           flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
+                    | ME_IO_SINGLE_CONFIG_DIO_WORD |
+                    ME_IO_SINGLE_CONFIG_DIO_DWORD);
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inb(instance->ctrl_reg);
+       switch (size) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                               mode &=
+                                   ~((ME0600_DIO_CONFIG_BIT_OUT_0) <<
+                                     (instance->dio_idx * 2));
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               mode &=
+                                   ~((ME0600_DIO_CONFIG_BIT_OUT_0) <<
+                                     (instance->dio_idx * 2));
+                               mode |=
+                                   ME0600_DIO_CONFIG_BIT_OUT_0 << (instance->
+                                                                   dio_idx *
+                                                                   2);
+                       } else {
+                               PERROR
+                                   ("Invalid port configuration specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outb(mode, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, mode);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_dio_io_single_read(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int *value, int time_out, int flags)
+{
+       me0600_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
+                                            (instance->dio_idx * 2));
+
+                       if ((mode ==
+                            (ME0600_DIO_CONFIG_BIT_OUT_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value =
+                                   inb(instance->
+                                       port_reg) & (0x0001 << channel);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
+                                            (instance->dio_idx * 2));
+
+                       if ((mode ==
+                            (ME0600_DIO_CONFIG_BIT_OUT_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value = inb(instance->port_reg) & 0x00FF;
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+
+               break;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_dio_io_single_write(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int value, int time_out, int flags)
+{
+       me0600_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+       uint8_t byte;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME0600_DIO_CONFIG_BIT_OUT_0 <<
+                            (instance->dio_idx * 2))) {
+                               byte = inb(instance->port_reg);
+
+                               if (value)
+                                       byte |= 0x1 << channel;
+                               else
+                                       byte &= ~(0x1 << channel);
+
+                               outb(byte, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME0600_DIO_CONFIG_BIT_OUT_0 <<
+                            (instance->dio_idx * 2))) {
+                               outb(value, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+
+               break;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_dio_query_number_channels(me_subdevice_t * subdevice,
+                                           int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_dio_query_subdevice_type(me_subdevice_t * subdevice,
+                                          int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DIO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_dio_query_subdevice_caps(me_subdevice_t * subdevice,
+                                          int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_DIR_BYTE;
+       return ME_ERRNO_SUCCESS;
+}
+
+me0600_dio_subdevice_t *me0600_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock)
+{
+       me0600_dio_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0600_dio_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0600_dio_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save digital i/o index */
+       subdevice->dio_idx = dio_idx;
+
+       /* Save the subdevice index */
+       subdevice->ctrl_reg = reg_base + ME0600_DIO_CONFIG_REG;
+       subdevice->port_reg = reg_base + ME0600_DIO_PORT_REG + dio_idx;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0600_dio_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me0600_dio_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me0600_dio_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me0600_dio_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0600_dio_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0600_dio_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0600_dio_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0600_dio.h b/drivers/staging/meilhaus/me0600_dio.h
new file mode 100644 (file)
index 0000000..5d075c7
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * @file me0600_dio.h
+ *
+ * @brief ME-630 digital input/output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_DIO_H_
+#define _ME0600_DIO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me0600_dio_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+       unsigned int dio_idx;                   /**< The index of the digital i/o on the device. */
+
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Register to configure the port direction. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me0600_dio_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-630 digital input/ouput subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param dio_idx The index of the digital i/o port on the device.
+ * @param ctrl_reg_lock Spin lock protecting the control register.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0600_dio_subdevice_t *me0600_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_dio_reg.h b/drivers/staging/meilhaus/me0600_dio_reg.h
new file mode 100644 (file)
index 0000000..f116ea3
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * @file me0600_dio_reg.h
+ *
+ * @brief ME-630 digital input/output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_DIO_REG_H_
+#define _ME0600_DIO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME0600_DIO_CONFIG_REG          0x0007
+#define ME0600_DIO_PORT_0_REG          0x0008
+#define ME0600_DIO_PORT_1_REG          0x0009
+#define ME0600_DIO_PORT_REG                    ME0600_DIO_PORT_0_REG
+
+#define ME0600_DIO_CONFIG_BIT_OUT_0    0x0001
+#define ME0600_DIO_CONFIG_BIT_OUT_1    0x0004
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_ext_irq.c b/drivers/staging/meilhaus/me0600_ext_irq.c
new file mode 100644 (file)
index 0000000..a449ab2
--- /dev/null
@@ -0,0 +1,478 @@
+/**
+ * @file me0600_ext_irq.c
+ *
+ * @brief ME-630 external interrupt subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+#include "meids.h"
+#include "medebug.h"
+
+#include "meplx_reg.h"
+#include "me0600_ext_irq_reg.h"
+#include "me0600_ext_irq.h"
+
+/*
+ * Functions
+ */
+
+static int me0600_ext_irq_io_irq_start(struct me_subdevice *subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int irq_source,
+                                      int irq_edge, int irq_arg, int flags)
+{
+       me0600_ext_irq_subdevice_t *instance;
+       uint32_t tmp;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (instance->lintno > 1) {
+               PERROR("Wrong idx=%d.\n", instance->lintno);
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
+               PERROR("Invalid irq source specified.\n");
+               return ME_ERRNO_INVALID_IRQ_SOURCE;
+       }
+
+       if (irq_edge != ME_IRQ_EDGE_RISING) {
+               PERROR("Invalid irq edge specified.\n");
+               return ME_ERRNO_INVALID_IRQ_EDGE;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->intcsr_lock);
+       tmp = inl(instance->intcsr);
+       switch (instance->lintno) {
+       case 0:
+               tmp |=
+                   PLX_INTCSR_LOCAL_INT1_EN | PLX_INTCSR_LOCAL_INT1_POL |
+                   PLX_INTCSR_PCI_INT_EN;
+               break;
+       case 1:
+               tmp |=
+                   PLX_INTCSR_LOCAL_INT2_EN | PLX_INTCSR_LOCAL_INT2_POL |
+                   PLX_INTCSR_PCI_INT_EN;
+               break;
+       }
+       outl(tmp, instance->intcsr);
+       PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
+       spin_unlock(instance->intcsr_lock);
+       instance->rised = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ext_irq_io_irq_wait(struct me_subdevice *subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int *irq_count,
+                                     int *value, int time_out, int flags)
+{
+       me0600_ext_irq_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->rised <= 0) {
+               instance->rised = 0;
+
+               if (time_out) {
+                       t = wait_event_interruptible_timeout(instance->
+                                                            wait_queue,
+                                                            (instance->rised !=
+                                                             0), t);
+
+                       if (t == 0) {
+                               PERROR("Wait on interrupt timed out.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               } else {
+                       wait_event_interruptible(instance->wait_queue,
+                                                (instance->rised != 0));
+               }
+
+               if (instance->rised < 0) {
+                       PERROR("Wait on interrupt aborted by user.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+       }
+
+       if (signal_pending(current)) {
+               PERROR("Wait on interrupt aborted by signal.\n");
+               err = ME_ERRNO_SIGNAL;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       instance->rised = 0;
+       *irq_count = instance->n;
+       *value = 1;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_ext_irq_io_irq_stop(struct me_subdevice *subdevice,
+                                     struct file *filep,
+                                     int channel, int flags)
+{
+       me0600_ext_irq_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t tmp;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (instance->lintno > 1) {
+               PERROR("Wrong idx=%d.\n", instance->lintno);
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->intcsr_lock);
+       tmp = inl(instance->intcsr);
+       switch (instance->lintno) {
+       case 0:
+               tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
+               break;
+       case 1:
+               tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
+               break;
+       }
+       outl(tmp, instance->intcsr);
+       PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
+       spin_unlock(instance->intcsr_lock);
+       instance->rised = -1;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_ext_irq_io_reset_subdevice(struct me_subdevice *subdevice,
+                                            struct file *filep, int flags)
+{
+       me0600_ext_irq_subdevice_t *instance;
+       uint32_t tmp;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->intcsr_lock);
+       tmp = inl(instance->intcsr);
+       switch (instance->lintno) {
+       case 0:
+               tmp |= PLX_INTCSR_LOCAL_INT1_POL | PLX_INTCSR_PCI_INT_EN;
+               tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
+               break;
+       case 1:
+               tmp |= PLX_INTCSR_LOCAL_INT2_POL | PLX_INTCSR_PCI_INT_EN;
+               tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
+               break;
+       }
+       outl(tmp, instance->intcsr);
+       PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
+       spin_unlock(instance->intcsr_lock);
+
+       instance->rised = -1;
+       instance->n = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ext_irq_query_number_channels(struct me_subdevice *subdevice,
+                                               int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 1;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ext_irq_query_subdevice_type(struct me_subdevice *subdevice,
+                                              int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_EXT_IRQ;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ext_irq_query_subdevice_caps(struct me_subdevice *subdevice,
+                                              int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_EXT_IRQ_EDGE_RISING;
+       return ME_ERRNO_SUCCESS;
+}
+
+static void me0600_ext_irq_destructor(struct me_subdevice *subdevice)
+{
+       me0600_ext_irq_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ext_irq_subdevice_t *) subdevice;
+
+       free_irq(instance->irq, (void *)instance);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me0600_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       me0600_ext_irq_subdevice_t *instance;
+       uint32_t status;
+       uint32_t mask = PLX_INTCSR_PCI_INT_EN;
+       irqreturn_t ret = IRQ_HANDLED;
+
+       instance = (me0600_ext_irq_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       PDEBUG("executed.\n");
+
+       if (instance->lintno > 1) {
+               PERROR_CRITICAL
+                   ("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n",
+                    __FUNCTION__, instance->lintno, inl(instance->intcsr));
+               return IRQ_NONE;
+       }
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->intcsr_lock);
+       status = inl(instance->intcsr);
+       switch (instance->lintno) {
+       case 0:
+               mask |= PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_LOCAL_INT1_EN;
+               break;
+       case 1:
+               mask |= PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_LOCAL_INT2_EN;
+               break;
+       }
+
+       if ((status & mask) == mask) {
+               instance->rised = 1;
+               instance->n++;
+               inb(instance->reset_reg);
+               PDEBUG("Interrupt detected.\n");
+       } else {
+               PINFO
+                   ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
+                    jiffies, __FUNCTION__, status);
+               ret = IRQ_NONE;
+       }
+       spin_unlock(instance->intcsr_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return ret;
+}
+
+me0600_ext_irq_subdevice_t *me0600_ext_irq_constructor(uint32_t plx_reg_base,
+                                                      uint32_t me0600_reg_base,
+                                                      spinlock_t * intcsr_lock,
+                                                      unsigned ext_irq_idx,
+                                                      int irq)
+{
+       me0600_ext_irq_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0600_ext_irq_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for 630_ext_irq instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0600_ext_irq_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->intcsr_lock = intcsr_lock;
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       subdevice->lintno = ext_irq_idx;
+
+       /* Request interrupt line */
+       subdevice->irq = irq;
+
+       err = request_irq(subdevice->irq, me0600_isr,
+#ifdef IRQF_DISABLED
+                         IRQF_DISABLED | IRQF_SHARED,
+#else
+                         SA_INTERRUPT | SA_SHIRQ,
+#endif
+                         ME0600_NAME, (void *)subdevice);
+
+       if (err) {
+               PERROR("Cannot get interrupt line.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       PINFO("Registered irq=%d.\n", subdevice->irq);
+
+       /* Initialize registers */
+       subdevice->intcsr = plx_reg_base + PLX_INTCSR;
+       subdevice->reset_reg =
+           me0600_reg_base + ME0600_INT_0_RESET_REG + ext_irq_idx;
+
+       /* Initialize the subdevice methods */
+       subdevice->base.me_subdevice_io_irq_start = me0600_ext_irq_io_irq_start;
+       subdevice->base.me_subdevice_io_irq_wait = me0600_ext_irq_io_irq_wait;
+       subdevice->base.me_subdevice_io_irq_stop = me0600_ext_irq_io_irq_stop;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0600_ext_irq_io_reset_subdevice;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0600_ext_irq_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0600_ext_irq_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0600_ext_irq_query_subdevice_caps;
+       subdevice->base.me_subdevice_destructor = me0600_ext_irq_destructor;
+
+       subdevice->rised = 0;
+       subdevice->n = 0;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0600_ext_irq.h b/drivers/staging/meilhaus/me0600_ext_irq.h
new file mode 100644 (file)
index 0000000..f5f2204
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * @file me0600_ext_irq.h
+ *
+ * @brief ME-630 external interrupt implementation.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME0600_EXT_IRQ_H_
+#define _ME0600_EXT_IRQ_H_
+
+#include <linux/sched.h>
+
+#include "mesubdevice.h"
+#include "meslock.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The ME-630 external interrupt subdevice class.
+ */
+typedef struct me0600_ext_irq_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *intcsr_lock;                /**< Spin lock to protect #intcsr. */
+
+       wait_queue_head_t wait_queue;           /**< Queue to put on threads waiting for an interrupt. */
+
+       int irq;                                /**< The irq number assigned by PCI BIOS. */
+       int rised;                              /**< If true an interrupt has occured. */
+       unsigned int n;                         /**< The number of interrupt since the driver was loaded. */
+       unsigned int lintno;                    /**< The number of the local PCI interrupt. */
+
+       uint32_t intcsr;                        /**< The PLX interrupt control and status register. */
+       uint32_t reset_reg;                     /**< The control register. */
+} me0600_ext_irq_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-630 external interrupt instance.
+ *
+ * @param plx_reg_base The register base address of the PLX chip as returned by the PCI BIOS.
+ * @param me0600_reg_base The register base address of the ME-630 device as returned by the PCI BIOS.
+ * @param irq The irq assigned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0600_ext_irq_subdevice_t *me0600_ext_irq_constructor(uint32_t plx_reg_base,
+                                                      uint32_t me0600_reg_base,
+                                                      spinlock_t * intcsr_lock,
+                                                      unsigned int ext_irq_idx,
+                                                      int irq);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_ext_irq_reg.h b/drivers/staging/meilhaus/me0600_ext_irq_reg.h
new file mode 100644 (file)
index 0000000..f6198fa
--- /dev/null
@@ -0,0 +1,18 @@
+/**
+ * @file me0600_ext_irq_reg.h
+ *
+ * @brief ME-630 external interrupt register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME0600_EXT_IRQ_REG_H_
+#define _ME0600_EXT_IRQ_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME0600_INT_0_RESET_REG     0x0005
+#define ME0600_INT_1_RESET_REG     0x0006
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_optoi.c b/drivers/staging/meilhaus/me0600_optoi.c
new file mode 100644 (file)
index 0000000..b6d977f
--- /dev/null
@@ -0,0 +1,243 @@
+/**
+ * @file me0600_optoi.c
+ *
+ * @brief ME-630 Optoisolated input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me0600_optoi_reg.h"
+#include "me0600_optoi.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me0600_optoi_io_reset_subdevice(struct me_subdevice *subdevice,
+                                          struct file *filep, int flags)
+{
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       PDEBUG("executed.\n");
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_optoi_io_single_config(me_subdevice_t * subdevice,
+                                        struct file *filep,
+                                        int channel,
+                                        int single_config,
+                                        int ref,
+                                        int trig_chan,
+                                        int trig_type,
+                                        int trig_edge, int flags)
+{
+       me0600_optoi_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_optoi_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config != ME_SINGLE_CONFIG_DIO_INPUT) {
+                               PERROR("Invalid port direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+
+               break;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_optoi_io_single_read(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int *value, int time_out, int flags)
+{
+       me0600_optoi_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_optoi_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inb(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_optoi_query_number_channels(me_subdevice_t * subdevice,
+                                             int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_optoi_query_subdevice_type(me_subdevice_t * subdevice,
+                                            int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DI;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_optoi_query_subdevice_caps(me_subdevice_t * subdevice,
+                                            int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me0600_optoi_subdevice_t *me0600_optoi_constructor(uint32_t reg_base)
+{
+       me0600_optoi_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0600_optoi_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0600_optoi_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       /* Save the subdevice index */
+       subdevice->port_reg = reg_base + ME0600_OPTO_INPUT_REG;
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0600_optoi_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me0600_optoi_io_single_config;
+       subdevice->base.me_subdevice_io_single_read =
+           me0600_optoi_io_single_read;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0600_optoi_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0600_optoi_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0600_optoi_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0600_optoi.h b/drivers/staging/meilhaus/me0600_optoi.h
new file mode 100644 (file)
index 0000000..e7e69bc
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * @file me0600_optoi.h
+ *
+ * @brief ME-630 Optoisolated input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_OPTOI_H_
+#define _ME0600_OPTOI_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me0600_optoi_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       uint32_t port_reg;                      /**< Register holding the port status. */
+} me0600_optoi_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-630 Optoisolated input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0600_optoi_subdevice_t *me0600_optoi_constructor(uint32_t reg_base);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_optoi_reg.h b/drivers/staging/meilhaus/me0600_optoi_reg.h
new file mode 100644 (file)
index 0000000..e0bc450
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * @file me0600_optoi_reg.h
+ *
+ * @brief ME-630 Optoisolated input subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_OPTOI_REG_H_
+#define _ME0600_OPTOI_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME0600_OPTO_INPUT_REG      0x0004
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_relay.c b/drivers/staging/meilhaus/me0600_relay.c
new file mode 100644 (file)
index 0000000..2665c69
--- /dev/null
@@ -0,0 +1,359 @@
+/**
+ * @file me0600_relay.c
+ *
+ * @brief ME-630 relay subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me0600_relay_reg.h"
+#include "me0600_relay.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me0600_relay_io_reset_subdevice(struct me_subdevice *subdevice,
+                                          struct file *filep, int flags)
+{
+       me0600_relay_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_relay_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       outb(0x0, instance->port_0_reg);
+       PDEBUG_REG("port_0_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_0_reg - instance->reg_base, 0);
+       outb(0x0, instance->port_1_reg);
+       PDEBUG_REG("port_1_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_1_reg - instance->reg_base, 0);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_relay_io_single_config(me_subdevice_t * subdevice,
+                                        struct file *filep,
+                                        int channel,
+                                        int single_config,
+                                        int ref,
+                                        int trig_chan,
+                                        int trig_type,
+                                        int trig_edge, int flags)
+{
+       me0600_relay_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_relay_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_WORD:
+               if (channel == 0) {
+                       if (single_config != ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               PERROR("Invalid word direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+
+               break;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_relay_io_single_read(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int *value, int time_out, int flags)
+{
+       me0600_relay_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_relay_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inb(instance->port_0_reg) & (0x1 << channel);
+               } else if ((channel >= 8) && (channel < 16)) {
+                       *value =
+                           inb(instance->port_1_reg) & (0x1 << (channel - 8));
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inb(instance->port_0_reg);
+               } else if (channel == 1) {
+                       *value = inb(instance->port_1_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if (channel == 0) {
+                       *value = (uint32_t) inb(instance->port_1_reg) << 8;
+                       *value |= inb(instance->port_0_reg);
+               } else {
+                       PERROR("Invalid word number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_relay_io_single_write(me_subdevice_t * subdevice,
+                                       struct file *filep,
+                                       int channel,
+                                       int value, int time_out, int flags)
+{
+       me0600_relay_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t state;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_relay_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       state = inb(instance->port_0_reg);
+                       state =
+                           value ? (state | (0x1 << channel)) : (state &
+                                                                 ~(0x1 <<
+                                                                   channel));
+                       outb(state, instance->port_0_reg);
+               } else if ((channel >= 8) && (channel < 16)) {
+                       state = inb(instance->port_1_reg);
+                       state =
+                           value ? (state | (0x1 << (channel - 8))) : (state &
+                                                                       ~(0x1 <<
+                                                                         (channel
+                                                                          -
+                                                                          8)));
+                       outb(state, instance->port_1_reg);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       outb(value, instance->port_0_reg);
+               } else if (channel == 1) {
+                       outb(value, instance->port_1_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if (channel == 0) {
+                       outb(value, instance->port_0_reg);
+                       outb(value >> 8, instance->port_1_reg);
+               } else {
+                       PERROR("Invalid word number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+               break;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_relay_query_number_channels(me_subdevice_t * subdevice,
+                                             int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 16;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_relay_query_subdevice_type(me_subdevice_t * subdevice,
+                                            int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_relay_query_subdevice_caps(me_subdevice_t * subdevice,
+                                            int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me0600_relay_subdevice_t *me0600_relay_constructor(uint32_t reg_base)
+{
+       me0600_relay_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0600_relay_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0600_relay_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       /* Save the subdevice index */
+       subdevice->port_0_reg = reg_base + ME0600_RELAIS_0_REG;
+       subdevice->port_1_reg = reg_base + ME0600_RELAIS_1_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0600_relay_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me0600_relay_io_single_config;
+       subdevice->base.me_subdevice_io_single_read =
+           me0600_relay_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me0600_relay_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0600_relay_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0600_relay_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0600_relay_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0600_relay.h b/drivers/staging/meilhaus/me0600_relay.h
new file mode 100644 (file)
index 0000000..2ce7dca
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * @file me0600_relay.h
+ *
+ * @brief ME-630 relay subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_RELAY_H_
+#define _ME0600_RELAY_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me0600_relay_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       unsigned long port_0_reg;                       /**< Register holding the port status. */
+       unsigned long port_1_reg;                       /**< Register holding the port status. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me0600_relay_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-630 relay subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param ctrl_reg_lock Spin lock protecting the control register.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0600_relay_subdevice_t *me0600_relay_constructor(uint32_t reg_base);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_relay_reg.h b/drivers/staging/meilhaus/me0600_relay_reg.h
new file mode 100644 (file)
index 0000000..ba4db2e
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * @file me0600_relay_reg.h
+ *
+ * @brief ME-630 relay subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_RELAY_REG_H_
+#define _ME0600_RELAY_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME0600_RELAIS_0_REG        0x0001
+#define ME0600_RELAIS_1_REG        0x0002
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_ttli.c b/drivers/staging/meilhaus/me0600_ttli.c
new file mode 100644 (file)
index 0000000..ab8e13b
--- /dev/null
@@ -0,0 +1,238 @@
+/**
+ * @file me0600_ttli.c
+ *
+ * @brief ME-630 TTL input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me0600_ttli_reg.h"
+#include "me0600_ttli.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me0600_ttli_io_reset_subdevice(struct me_subdevice *subdevice,
+                                         struct file *filep, int flags)
+{
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       PDEBUG("executed.\n");
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ttli_io_single_config(me_subdevice_t * subdevice,
+                                       struct file *filep,
+                                       int channel,
+                                       int single_config,
+                                       int ref,
+                                       int trig_chan,
+                                       int trig_type, int trig_edge, int flags)
+{
+       me0600_ttli_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ttli_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config != ME_SINGLE_CONFIG_DIO_INPUT) {
+                               PERROR("Invalid port direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+
+               break;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_ttli_io_single_read(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int *value, int time_out, int flags)
+{
+       me0600_ttli_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0600_ttli_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inb(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0600_ttli_query_number_channels(me_subdevice_t * subdevice,
+                                            int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ttli_query_subdevice_type(me_subdevice_t * subdevice,
+                                           int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DI;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0600_ttli_query_subdevice_caps(me_subdevice_t * subdevice,
+                                           int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me0600_ttli_subdevice_t *me0600_ttli_constructor(uint32_t reg_base)
+{
+       me0600_ttli_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0600_ttli_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0600_ttli_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       /* Save the subdevice index */
+       subdevice->port_reg = reg_base + ME0600_TTL_INPUT_REG;
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0600_ttli_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me0600_ttli_io_single_config;
+       subdevice->base.me_subdevice_io_single_read =
+           me0600_ttli_io_single_read;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0600_ttli_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0600_ttli_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0600_ttli_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0600_ttli.h b/drivers/staging/meilhaus/me0600_ttli.h
new file mode 100644 (file)
index 0000000..6c90396
--- /dev/null
@@ -0,0 +1,58 @@
+/**
+ * @file me0600_ttli.h
+ *
+ * @brief ME-630 TTL input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_TTLI_H_
+#define _ME0600_TTLI_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me0600_ttli_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       uint32_t port_reg;                      /**< Register holding the port status. */
+} me0600_ttli_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-630 TTL input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0600_ttli_subdevice_t *me0600_ttli_constructor(uint32_t reg_base);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0600_ttli_reg.h b/drivers/staging/meilhaus/me0600_ttli_reg.h
new file mode 100644 (file)
index 0000000..4f986d1
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * @file me0600_ttli_reg.h
+ *
+ * @brief ME-630 TTL input subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0600_TTLI_REG_H_
+#define _ME0600_TTLI_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME0600_TTL_INPUT_REG       0x0003
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0900_device.c b/drivers/staging/meilhaus/me0900_device.c
new file mode 100644 (file)
index 0000000..764d5d3
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * @file me0900_device.c
+ *
+ * @brief ME-9x device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+*/
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "me0900_device.h"
+#include "me0900_reg.h"
+#include "mesubdevice.h"
+#include "me0900_do.h"
+#include "me0900_di.h"
+
+me_device_t *me0900_pci_constructor(struct pci_dev *pci_device)
+{
+       me0900_device_t *me0900_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+       int port_shift;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me0900_device = kmalloc(sizeof(me0900_device_t), GFP_KERNEL);
+
+       if (!me0900_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(me0900_device, 0, sizeof(me0900_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me0900_device, pci_device);
+
+       if (err) {
+               kfree(me0900_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           me0900_versions_get_device_index(me0900_device->base.info.pci.
+                                            device_id);
+
+       /* Initialize 8255 chip to desired mode */
+       if (me0900_device->base.info.pci.device_id ==
+           PCI_DEVICE_ID_MEILHAUS_ME0940) {
+               outb(0x9B,
+                    me0900_device->base.info.pci.reg_bases[2] +
+                    ME0900_CTRL_REG);
+       } else if (me0900_device->base.info.pci.device_id ==
+                  PCI_DEVICE_ID_MEILHAUS_ME0950) {
+               outb(0x89,
+                    me0900_device->base.info.pci.reg_bases[2] +
+                    ME0900_CTRL_REG);
+               outb(0x00,
+                    me0900_device->base.info.pci.reg_bases[2] +
+                    ME0900_WRITE_ENABLE_REG);
+       } else if (me0900_device->base.info.pci.device_id ==
+                  PCI_DEVICE_ID_MEILHAUS_ME0960) {
+               outb(0x8B,
+                    me0900_device->base.info.pci.reg_bases[2] +
+                    ME0900_CTRL_REG);
+               outb(0x00,
+                    me0900_device->base.info.pci.reg_bases[2] +
+                    ME0900_WRITE_ENABLE_REG);
+       }
+
+       port_shift =
+           (me0900_device->base.info.pci.device_id ==
+            PCI_DEVICE_ID_MEILHAUS_ME0960) ? 1 : 0;
+       // Create subdevice instances.
+
+       for (i = 0; i < me0900_versions[version_idx].di_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me0900_di_constructor(me0900_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            i + port_shift);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0900_device);
+                       kfree(me0900_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0900_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me0900_versions[version_idx].do_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me0900_do_constructor(me0900_device->
+                                                            base.info.pci.
+                                                            reg_bases[2], i);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me0900_device);
+                       kfree(me0900_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me0900_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me0900_device;
+}
+
+// Init and exit of module.
+
+static int __init me0900_init(void)
+{
+       PDEBUG("executed.\n.");
+       return 0;
+}
+
+static void __exit me0900_exit(void)
+{
+       PDEBUG("executed.\n.");
+}
+
+module_init(me0900_init);
+module_exit(me0900_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for ME-9x Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-9x Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me0900_pci_constructor);
diff --git a/drivers/staging/meilhaus/me0900_device.h b/drivers/staging/meilhaus/me0900_device.h
new file mode 100644 (file)
index 0000000..bd17f25
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * @file me0900_device.h
+ *
+ * @brief ME-0900 (ME-9x) device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0900_DEVICE_H
+#define _ME0900_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding ME-0900 (ME-9x) device capabilities.
+ */
+typedef struct me0900_version {
+       uint16_t device_id;
+       unsigned int di_subdevices;
+       unsigned int do_subdevices;
+} me0900_version_t;
+
+/**
+ * @brief Device capabilities.
+ */
+static me0900_version_t me0900_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME0940, 2, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME0950, 0, 2},
+       {PCI_DEVICE_ID_MEILHAUS_ME0960, 1, 1},
+       {0},
+};
+
+#define ME0900_DEVICE_VERSIONS (sizeof(me0900_versions) / sizeof(me0900_version_t) - 1)        /**< Returns the number of entries in #me0900_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me0900_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me0900_versions.
+ */
+static inline unsigned int me0900_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME0900_DEVICE_VERSIONS; i++)
+               if (me0900_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-0900 (ME-9x) device class structure.
+ */
+typedef struct me0900_device {
+       me_device_t base;                       /**< The Meilhaus device base class. */
+} me0900_device_t;
+
+/**
+ * @brief The ME-9x device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-0900 (ME-9x) device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me0900_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0900_di.c b/drivers/staging/meilhaus/me0900_di.c
new file mode 100644 (file)
index 0000000..d7d7394
--- /dev/null
@@ -0,0 +1,246 @@
+/**
+ * @file me0900_di.c
+ *
+ * @brief ME-9x digital input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "meids.h"
+#include "medebug.h"
+#include "meplx_reg.h"
+#include "me0900_reg.h"
+#include "me0900_di.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me0900_di_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       PDEBUG("executed.\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0900_di_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me0900_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0900_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                       } else {
+                               PERROR("Invalid byte direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0900_di_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me0900_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0900_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = (~inb(instance->port_reg)) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = ~inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0900_di_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0900_di_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DI;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0900_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me0900_di_subdevice_t *me0900_di_constructor(uint32_t reg_base,
+                                            unsigned int di_idx)
+{
+       me0900_di_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0900_di_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0900_di_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       /* Save the subdevice index. */
+       subdevice->di_idx = di_idx;
+
+       /* Initialize registers */
+       if (di_idx == 0) {
+               subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
+               subdevice->port_reg = reg_base + ME0900_PORT_A_REG;
+       } else {
+               subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
+               subdevice->port_reg = reg_base + ME0900_PORT_B_REG;
+       }
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0900_di_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me0900_di_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me0900_di_io_single_read;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0900_di_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0900_di_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0900_di_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0900_di.h b/drivers/staging/meilhaus/me0900_di.h
new file mode 100644 (file)
index 0000000..014f134
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * @file me0900_di.h
+ *
+ * @brief ME-9x digital input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0900_DI_H_
+#define _ME0900_DI_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me0900_di_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       unsigned int di_idx;
+
+       unsigned long ctrl_reg;
+       unsigned long port_reg;
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me0900_di_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-9x digital input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0900_di_subdevice_t *me0900_di_constructor(uint32_t me0900_reg_base,
+                                            unsigned int di_idx);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0900_do.c b/drivers/staging/meilhaus/me0900_do.c
new file mode 100644 (file)
index 0000000..b5b9c3a
--- /dev/null
@@ -0,0 +1,314 @@
+/**
+ * @file me0900_do.c
+ *
+ * @brief ME-9x digital output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me0900_reg.h"
+#include "me0900_do.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me0900_do_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me0900_do_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0900_do_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       outb(0xFF, instance->port_reg);
+       PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0xff);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0900_do_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me0900_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0900_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                       } else {
+                               PERROR("Invalid byte direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0900_do_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me0900_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0900_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = (~inb(instance->port_reg)) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = ~inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0900_do_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me0900_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long state;
+
+       PDEBUG("executed.\n");
+
+       instance = (me0900_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       state = inb(instance->port_reg);
+                       state =
+                           (!value) ? (state | (0x1 << channel)) : (state &
+                                                                    ~(0x1 <<
+                                                                      channel));
+                       outb(state, instance->port_reg);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       outb(~(value), instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me0900_do_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0900_do_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me0900_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me0900_do_subdevice_t *me0900_do_constructor(uint32_t reg_base,
+                                            unsigned int do_idx)
+{
+       me0900_do_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me0900_do_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me0900_do_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       /* Save the subdevice index */
+       subdevice->do_idx = do_idx;
+
+       /* Initialize registers */
+       if (do_idx == 0) {
+               subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
+               subdevice->port_reg = reg_base + ME0900_PORT_A_REG;
+               subdevice->enable_reg = reg_base + ME0900_WRITE_ENABLE_REG;
+               subdevice->disable_reg = reg_base + ME0900_WRITE_DISABLE_REG;
+       } else {
+               subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
+               subdevice->port_reg = reg_base + ME0900_PORT_B_REG;
+               subdevice->enable_reg = reg_base + ME0900_WRITE_ENABLE_REG;
+               subdevice->disable_reg = reg_base + ME0900_WRITE_DISABLE_REG;
+       }
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me0900_do_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me0900_do_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me0900_do_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me0900_do_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me0900_do_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me0900_do_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me0900_do_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me0900_do.h b/drivers/staging/meilhaus/me0900_do.h
new file mode 100644 (file)
index 0000000..13e8a8b
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * @file me0900_do.h
+ *
+ * @brief ME-9x digital output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0900_DO_H_
+#define _ME0900_DO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me0900_do_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       unsigned int do_idx;
+
+       unsigned long ctrl_reg;
+       unsigned long port_reg;
+       unsigned long enable_reg;
+       unsigned long disable_reg;
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me0900_do_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-9x digital output subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param do_idx The index of the digital output subdevice on this device.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me0900_do_subdevice_t *me0900_do_constructor(uint32_t reg_base,
+                                            unsigned int do_idx);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me0900_reg.h b/drivers/staging/meilhaus/me0900_reg.h
new file mode 100644 (file)
index 0000000..3bf163b
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * @file me0900_reg.h
+ *
+ * @brief ME-9x register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME0900_REG_H_
+#define _ME0900_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME0900_PORT_A_REG          0x00
+#define ME0900_PORT_B_REG          0x01
+#define ME0900_PORT_C_REG          0x02
+#define ME0900_CTRL_REG            0x03        // ( ,w)
+#define ME0900_WRITE_ENABLE_REG    0x04        // (r,w)
+#define ME0900_WRITE_DISABLE_REG   0x08        // (r,w)
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me1000_device.c b/drivers/staging/meilhaus/me1000_device.c
new file mode 100644 (file)
index 0000000..c44e214
--- /dev/null
@@ -0,0 +1,208 @@
+/**
+ * @file me1000_device.c
+ *
+ * @brief ME-1000 device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "me1000_device.h"
+#include "mesubdevice.h"
+#include "me1000_dio.h"
+
+static int me1000_config_load(me_device_t * me_device, struct file *filep,
+                             me_cfg_device_entry_t * config)
+{
+       me1000_device_t *me1000_device;
+       me1000_dio_subdevice_t *dio;
+
+       PDEBUG("executed.\n");
+
+       me1000_device = (me1000_device_t *) me_device;
+
+       if (config->count == 2) {
+               if (me_slist_get_number_subdevices(&me1000_device->base.slist)
+                   == 2) {
+                       // Nothing to do.
+               } else {
+                       // Remove 2 extra subdevices
+                       dio =
+                           (me1000_dio_subdevice_t *)
+                           me_slist_del_subdevice_tail(&me1000_device->base.
+                                                       slist);
+                       if (dio)
+                               dio->base.
+                                   me_subdevice_destructor((me_subdevice_t *)
+                                                           dio);
+
+                       dio =
+                           (me1000_dio_subdevice_t *)
+                           me_slist_del_subdevice_tail(&me1000_device->base.
+                                                       slist);
+                       if (dio)
+                               dio->base.
+                                   me_subdevice_destructor((me_subdevice_t *)
+                                                           dio);
+               }
+       } else if (config->count == 4) {
+               //Add 2 subdevices
+               if (me_slist_get_number_subdevices(&me1000_device->base.slist)
+                   == 2) {
+                       dio =
+                           me1000_dio_constructor(me1000_device->base.info.pci.
+                                                  reg_bases[2], 2,
+                                                  &me1000_device->ctrl_lock);
+                       if (!dio) {
+                               PERROR("Cannot create dio subdevice.\n");
+                               return ME_ERRNO_INTERNAL;
+                       }
+                       me_slist_add_subdevice_tail(&me1000_device->base.slist,
+                                                   (me_subdevice_t *) dio);
+
+                       dio =
+                           me1000_dio_constructor(me1000_device->base.info.pci.
+                                                  reg_bases[2], 3,
+                                                  &me1000_device->ctrl_lock);
+                       if (!dio) {
+                               dio =
+                                   (me1000_dio_subdevice_t *)
+                                   me_slist_del_subdevice_tail(&me1000_device->
+                                                               base.slist);
+                               if (dio)
+                                       dio->base.
+                                           me_subdevice_destructor((me_subdevice_t *) dio);
+
+                               PERROR("Cannot create dio subdevice.\n");
+                               return ME_ERRNO_INTERNAL;
+                       }
+                       me_slist_add_subdevice_tail(&me1000_device->base.slist,
+                                                   (me_subdevice_t *) dio);
+               } else {
+                       // Nothing to do.
+               }
+       } else {
+               PERROR("Invalid configuration.\n");
+               return ME_ERRNO_INTERNAL;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+me_device_t *me1000_pci_constructor(struct pci_dev * pci_device)
+{
+       me1000_device_t *me1000_device;
+       me_subdevice_t *subdevice;
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me1000_device = kmalloc(sizeof(me1000_device_t), GFP_KERNEL);
+
+       if (!me1000_device) {
+               PERROR("Cannot get memory for ME-1000 device instance.\n");
+               return NULL;
+       }
+
+       memset(me1000_device, 0, sizeof(me1000_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me1000_device, pci_device);
+
+       if (err) {
+               kfree(me1000_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+       // Initialize spin lock .
+       spin_lock_init(&me1000_device->ctrl_lock);
+
+       for (i = 0; i < 4; i++) {
+               subdevice =
+                   (me_subdevice_t *) me1000_dio_constructor(me1000_device->
+                                                             base.info.pci.
+                                                             reg_bases[2], i,
+                                                             &me1000_device->
+                                                             ctrl_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me1000_device);
+                       kfree(me1000_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me1000_device->base.slist,
+                                           subdevice);
+       }
+
+       // Overwrite base class methods.
+       me1000_device->base.me_device_config_load = me1000_config_load;
+
+       return (me_device_t *) me1000_device;
+}
+
+// Init and exit of module.
+static int __init me1000_init(void)
+{
+       PDEBUG("executed.\n");
+       return 0;
+}
+
+static void __exit me1000_exit(void)
+{
+       PDEBUG("executed.\n");
+}
+
+module_init(me1000_init);
+module_exit(me1000_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for Meilhaus ME-1000 Devices");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-1000 Digital I/O Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me1000_pci_constructor);
diff --git a/drivers/staging/meilhaus/me1000_device.h b/drivers/staging/meilhaus/me1000_device.h
new file mode 100644 (file)
index 0000000..cbbe126
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * @file me1000_device.h
+ *
+ * @brief ME-1000 device class instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1000_H_
+#define _ME1000_H_
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+#define ME1000_MAGIC_NUMBER    1000
+
+/**
+ * @brief The ME-1000 device class structure.
+ */
+typedef struct me1000_device {
+       me_device_t base;               /**< The Meilhaus device base class. */
+       spinlock_t ctrl_lock;           /**< Guards the DIO mode register. */
+} me1000_device_t;
+
+/**
+ * @brief The ME-1000 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-1000 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me1000_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me1000_dio.c b/drivers/staging/meilhaus/me1000_dio.c
new file mode 100644 (file)
index 0000000..87605a9
--- /dev/null
@@ -0,0 +1,438 @@
+/**
+ * @file me1000_dio.c
+ *
+ * @brief ME-1000 DIO subdevice instance.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+#include "medebug.h"
+
+#include "me1000_dio_reg.h"
+#include "me1000_dio.h"
+
+/*
+ * Defines
+ */
+#define ME1000_DIO_MAGIC_NUMBER        0x1000  /**< The magic number of the class structure. */
+
+/*
+ * Functions
+ */
+
+static int me1000_dio_io_reset_subdevice(struct me_subdevice *subdevice,
+                                        struct file *filep, int flags)
+{
+       me1000_dio_subdevice_t *instance;
+       uint32_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1000_dio_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       tmp = inl(instance->ctrl_reg);
+       tmp &= ~(0x1 << instance->dio_idx);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outl(0x00000000, instance->port_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, 0);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1000_dio_io_single_config(struct me_subdevice *subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int single_config,
+                                      int ref,
+                                      int trig_chan,
+                                      int trig_type, int trig_edge, int flags)
+{
+       me1000_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       int ctrl;
+       int size =
+           flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
+                    | ME_IO_SINGLE_CONFIG_DIO_WORD |
+                    ME_IO_SINGLE_CONFIG_DIO_DWORD);
+
+       PDEBUG("executed.\n");
+
+       instance = (me1000_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       ctrl = inl(instance->ctrl_reg);
+
+       switch (size) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_DWORD:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                               ctrl &= ~(0x1 << instance->dio_idx);
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               ctrl |= 0x1 << instance->dio_idx;
+                       } else {
+                               PERROR("Invalid port direction.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1000_dio_io_single_read(struct me_subdevice *subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int *value, int time_out, int flags)
+{
+       me1000_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1000_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 32)) {
+                       *value = inl(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if ((channel >= 0) && (channel < 4)) {
+                       *value =
+                           (inl(instance->port_reg) >> (channel * 8)) & 0xFF;
+               } else {
+                       PERROR("Invalid byte number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if ((channel >= 0) && (channel < 2)) {
+                       *value =
+                           (inl(instance->port_reg) >> (channel * 16)) &
+                           0xFFFF;
+               } else {
+                       PERROR("Invalid word number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_DWORD:
+               if (channel == 0) {
+                       *value = inl(instance->port_reg);
+               } else {
+                       PERROR("Invalid dword number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1000_dio_io_single_write(struct me_subdevice *subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int value, int time_out, int flags)
+{
+       me1000_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t config;
+       uint32_t state;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1000_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       config = inl(instance->ctrl_reg) & (0x1 << instance->dio_idx);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 32)) {
+                       if (config) {
+                               state = inl(instance->port_reg);
+                               state =
+                                   value ? (state | (0x1 << channel)) : (state
+                                                                         &
+                                                                         ~(0x1
+                                                                           <<
+                                                                           channel));
+                               outl(state, instance->port_reg);
+                               PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->port_reg -
+                                          instance->reg_base, state);
+                       } else {
+                               PERROR("Port is not in output mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if ((channel >= 0) && (channel < 4)) {
+                       if (config) {
+                               state = inl(instance->port_reg);
+                               state &= ~(0xFF << (channel * 8));
+                               state |= (value & 0xFF) << (channel * 8);
+                               outl(state, instance->port_reg);
+                               PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->port_reg -
+                                          instance->reg_base, state);
+                       } else {
+                               PERROR("Port is not in output mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if ((channel >= 0) && (channel < 2)) {
+                       if (config) {
+                               state = inl(instance->port_reg);
+                               state &= ~(0xFFFF << (channel * 16));
+                               state |= (value & 0xFFFF) << (channel * 16);
+                               outl(state, instance->port_reg);
+                               PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->port_reg -
+                                          instance->reg_base, state);
+                       } else {
+                               PERROR("Port is not in output mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid word number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_DWORD:
+               if (channel == 0) {
+                       if (config) {
+                               outl(value, instance->port_reg);
+                               PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->port_reg -
+                                          instance->reg_base, value);
+                       } else {
+                               PERROR("Port is not in output mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid dword number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1000_dio_query_number_channels(struct me_subdevice *subdevice,
+                                           int *number)
+{
+       PDEBUG("executed.\n");
+       *number = ME1000_DIO_NUMBER_CHANNELS;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1000_dio_query_subdevice_type(struct me_subdevice *subdevice,
+                                          int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DIO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1000_dio_query_subdevice_caps(struct me_subdevice *subdevice,
+                                          int *caps)
+{
+       me1000_dio_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1000_dio_subdevice_t *) subdevice;
+
+       *caps = ME_CAPS_DIO_DIR_DWORD;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+me1000_dio_subdevice_t *me1000_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock)
+{
+       me1000_dio_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me1000_dio_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for ME-1000 DIO instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me1000_dio_subdevice_t));
+
+       /* Check if counter index is out of range */
+
+       if (dio_idx >= ME1000_DIO_NUMBER_PORTS) {
+               PERROR("DIO index is out of range.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the DIO index */
+       subdevice->dio_idx = dio_idx;
+
+       /* Initialize registers. */
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+       subdevice->ctrl_reg = reg_base + ME1000_PORT_MODE;
+       subdevice->port_reg =
+           reg_base + ME1000_PORT + (dio_idx * ME1000_PORT_STEP);
+
+       /* Override base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me1000_dio_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me1000_dio_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me1000_dio_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me1000_dio_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me1000_dio_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me1000_dio_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me1000_dio_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me1000_dio.h b/drivers/staging/meilhaus/me1000_dio.h
new file mode 100644 (file)
index 0000000..d26e93f
--- /dev/null
@@ -0,0 +1,71 @@
+/**
+ * @file me1000_dio.h
+ *
+ * @brief Meilhaus ME-1000 digital i/o implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1000_DIO_H_
+#define _ME1000_DIO_H_
+
+#include "mesubdevice.h"
+#include "meslock.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The ME-1000 DIO subdevice class.
+ */
+typedef struct me1000_dio_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+//      uint32_t magic;                                 /**< The magic number unique for this structure. */
+
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg and #ctrl_reg_mirror from concurrent access. */
+       int dio_idx;                                    /**< The index of the DIO port on the device. */
+
+       unsigned long port_reg;                 /**< Register to read or write a value from or to the port respectively. */
+       unsigned long ctrl_reg;                 /**< Register to configure the DIO modes. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me1000_dio_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-1000 DIO instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param dio_idx The index of the DIO on the device.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the control register and from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me1000_dio_subdevice_t *me1000_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me1000_dio_reg.h b/drivers/staging/meilhaus/me1000_dio_reg.h
new file mode 100644 (file)
index 0000000..4d5b38d
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * @file me1000_dio_reg.h
+ *
+ * @brief ME-1000 digital i/o register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1000_DIO_REG_H_
+# define _ME1000_DIO_REG_H_
+
+# ifdef __KERNEL__
+
+# define ME1000_DIO_NUMBER_CHANNELS    32                              /**< The number of channels per DIO port. */
+# define ME1000_DIO_NUMBER_PORTS       4                               /**< The number of ports per ME-1000. */
+
+// # define ME1000_PORT_A                               0x0000                  /**< Port A base register offset. */
+// # define ME1000_PORT_B                               0x0004                  /**< Port B base register offset. */
+// # define ME1000_PORT_C                               0x0008                  /**< Port C base register offset. */
+// # define ME1000_PORT_D                               0x000C                  /**< Port D base register offset. */
+# define ME1000_PORT                           0x0000                  /**< Base for port's register. */
+# define ME1000_PORT_STEP                      4                               /**< Distance between port's register. */
+
+# define ME1000_PORT_MODE                      0x0010                  /**< Configuration register to switch the port direction. */
+// # define ME1000_PORT_MODE_OUTPUT_A   (1 << 0)                /**< If set, port A is in output, otherwise in input mode. */
+// # define ME1000_PORT_MODE_OUTPUT_B   (1 << 1)                /**< If set, port B is in output, otherwise in input mode. */
+// # define ME1000_PORT_MODE_OUTPUT_C   (1 << 2)                /**< If set, port C is in output, otherwise in input mode. */
+// # define ME1000_PORT_MODE_OUTPUT_D   (1 << 3)                /**< If set, port D is in output, otherwise in input mode. */
+
+# endif        //__KERNEL__
+#endif //_ME1000_DIO_REG_H_
diff --git a/drivers/staging/meilhaus/me1400_device.c b/drivers/staging/meilhaus/me1400_device.c
new file mode 100644 (file)
index 0000000..b95bb4f
--- /dev/null
@@ -0,0 +1,256 @@
+/**
+ * @file me1400_device.c
+ *
+ * @brief ME-1400 device instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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.
+ */
+
+/*
+ * User application could also include the kernel header files. But the
+ * real kernel functions are protected by #ifdef __KERNEL__.
+ */
+#ifndef __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * This must be defined before module.h is included. Not needed, when
+ * it is a built in driver.
+ */
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+
+#include "me1400_device.h"
+#include "me8254.h"
+#include "me8254_reg.h"
+#include "me8255.h"
+#include "me1400_ext_irq.h"
+
+me_device_t *me1400_pci_constructor(struct pci_dev *pci_device)
+{
+       int err;
+       me1400_device_t *me1400_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       unsigned int me8255_idx;
+       unsigned int dio_idx;
+       unsigned int me8254_idx;
+       unsigned int ctr_idx;
+       unsigned int ext_irq_idx;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me1400_device = kmalloc(sizeof(me1400_device_t), GFP_KERNEL);
+
+       if (!me1400_device) {
+               PERROR("Cannot get memory for 1400ate device instance.\n");
+               return NULL;
+       }
+
+       memset(me1400_device, 0, sizeof(me1400_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me1400_device, pci_device);
+
+       if (err) {
+               kfree(me1400_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Check for ME1400 extension device. If detected we fake a ME-1400 D device id. */
+       if (me1400_device->base.info.pci.device_id ==
+           PCI_DEVICE_ID_MEILHAUS_ME140C) {
+               uint8_t ctrl;
+               ctrl =
+                   inb(me1400_device->base.info.pci.reg_bases[2] +
+                       ME1400D_CLK_SRC_2_REG);
+               PDEBUG_REG("xxx_reg inb(0x%X+0x%X)=0x%x\n",
+                          me1400_device->base.info.pci.reg_bases[2],
+                          ME1400D_CLK_SRC_2_REG, ctrl);
+               outb(ctrl | 0xF0,
+                    me1400_device->base.info.pci.reg_bases[2] +
+                    ME1400D_CLK_SRC_2_REG);
+               PDEBUG_REG("xxx_reg outb(0x%X+0x%X)=0x%x\n",
+                          me1400_device->base.info.pci.reg_bases[2],
+                          ME1400D_CLK_SRC_2_REG, ctrl | 0xF0);
+               ctrl =
+                   inb(me1400_device->base.info.pci.reg_bases[2] +
+                       ME1400D_CLK_SRC_2_REG);
+               PDEBUG_REG("xxx_reg inb(0x%X+0x%X)=0x%x\n",
+                          me1400_device->base.info.pci.reg_bases[2],
+                          ME1400D_CLK_SRC_2_REG, ctrl);
+
+               if ((ctrl & 0xF0) == 0xF0) {
+                       PINFO("ME1400 D detected.\n");
+                       me1400_device->base.info.pci.device_id =
+                           PCI_DEVICE_ID_MEILHAUS_ME140D;
+               }
+       }
+
+       /* Initialize global stuff of digital i/o subdevices. */
+       for (me8255_idx = 0; me8255_idx < ME1400_MAX_8255; me8255_idx++) {
+               me1400_device->dio_current_mode[me8255_idx] = 0;
+               spin_lock_init(&me1400_device->dio_ctrl_reg_lock[me8255_idx]);
+       }
+
+       /* Initialize global stuff of counter subdevices. */
+       spin_lock_init(&me1400_device->clk_src_reg_lock);
+
+       for (me8254_idx = 0; me8254_idx < ME1400_MAX_8254; me8254_idx++)
+               spin_lock_init(&me1400_device->ctr_ctrl_reg_lock[me8254_idx]);
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           me1400_versions_get_device_index(me1400_device->base.info.pci.
+                                            device_id);
+
+       /* Generate DIO subdevice instances. */
+       for (me8255_idx = 0;
+            me8255_idx < me1400_versions[version_idx].dio_chips;
+            me8255_idx++) {
+               for (dio_idx = 0; dio_idx < 3; dio_idx++) {
+                       subdevice =
+                           (me_subdevice_t *)
+                           me8255_constructor(me1400_versions[version_idx].
+                                              device_id,
+                                              me1400_device->base.info.pci.
+                                              reg_bases[2], me8255_idx,
+                                              dio_idx,
+                                              &me1400_device->
+                                              dio_current_mode[me8255_idx],
+                                              &me1400_device->
+                                              dio_ctrl_reg_lock[me8255_idx]);
+
+                       if (!subdevice) {
+                               me_device_deinit((me_device_t *) me1400_device);
+                               kfree(me1400_device);
+                               PERROR("Cannot get memory for subdevice.\n");
+                               return NULL;
+                       }
+
+                       me_slist_add_subdevice_tail(&me1400_device->base.slist,
+                                                   subdevice);
+               }
+       }
+
+       /* Generate counter subdevice instances. */
+       for (me8254_idx = 0;
+            me8254_idx < me1400_versions[version_idx].ctr_chips;
+            me8254_idx++) {
+               for (ctr_idx = 0; ctr_idx < 3; ctr_idx++) {
+                       subdevice =
+                           (me_subdevice_t *)
+                           me8254_constructor(me1400_device->base.info.pci.
+                                              device_id,
+                                              me1400_device->base.info.pci.
+                                              reg_bases[2], me8254_idx,
+                                              ctr_idx,
+                                              &me1400_device->
+                                              ctr_ctrl_reg_lock[me8254_idx],
+                                              &me1400_device->
+                                              clk_src_reg_lock);
+
+                       if (!subdevice) {
+                               me_device_deinit((me_device_t *) me1400_device);
+                               kfree(me1400_device);
+                               PERROR("Cannot get memory for subdevice.\n");
+                               return NULL;
+                       }
+
+                       me_slist_add_subdevice_tail(&me1400_device->base.slist,
+                                                   subdevice);
+               }
+       }
+
+       /* Generate external interrupt subdevice instances. */
+       for (ext_irq_idx = 0;
+            ext_irq_idx < me1400_versions[version_idx].ext_irq_subdevices;
+            ext_irq_idx++) {
+               subdevice =
+                   (me_subdevice_t *)
+                   me1400_ext_irq_constructor(me1400_device->base.info.pci.
+                                              device_id,
+                                              me1400_device->base.info.pci.
+                                              reg_bases[1],
+                                              me1400_device->base.info.pci.
+                                              reg_bases[2],
+                                              &me1400_device->clk_src_reg_lock,
+                                              me1400_device->base.irq);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me1400_device);
+                       kfree(me1400_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me1400_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me1400_device;
+}
+
+// Init and exit of module.
+
+static int __init me1400_init(void)
+{
+       PDEBUG("executed.\n");
+       return 0;
+}
+
+static void __exit me1400_exit(void)
+{
+       PDEBUG("executed.\n");
+}
+
+module_init(me1400_init);
+module_exit(me1400_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for Meilhaus ME-14xx devices");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-14xx MIO devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me1400_pci_constructor);
diff --git a/drivers/staging/meilhaus/me1400_device.h b/drivers/staging/meilhaus/me1400_device.h
new file mode 100644 (file)
index 0000000..6215b25
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * @file me1400_device.c
+ *
+ * @brief ME-1400 device family instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1400_DEVICE_H_
+#define _ME1400_DEVICE_H_
+
+#include "metypes.h"
+#include "medefines.h"
+#include "meinternal.h"
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure to store device capabilities.
+ */
+typedef struct me1400_version {
+       uint16_t device_id;                                     /**< The PCI device id of the device. */
+       unsigned int dio_chips;                         /**< The number of 8255 chips on the device. */
+       unsigned int ctr_chips;                         /**< The number of 8254 chips on the device. */
+       unsigned int ext_irq_subdevices;        /**< The number of external interrupt inputs on the device. */
+} me1400_version_t;
+
+/**
+  * @brief Defines for each ME-1400 device version its capabilities.
+ */
+static me1400_version_t me1400_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME1400, 1, 0, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME140A, 1, 1, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME140B, 2, 2, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME14E0, 1, 0, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME14EA, 1, 1, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME14EB, 2, 2, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME140C, 1, 5, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME140D, 2, 10, 1},
+       {0}
+};
+
+#define ME1400_DEVICE_VERSIONS (sizeof(me1400_versions) / sizeof(me1400_version_t) - 1)        /**< Returns the number of entries in #me1400_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me1400_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me1400_versions.
+ */
+static inline unsigned int me1400_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME1400_DEVICE_VERSIONS; i++)
+               if (me1400_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+#define ME1400_MAX_8254                10      /**< The maximum number of 8254 counter subdevices available on any ME-1400 device. */
+#define ME1400_MAX_8255                2       /**< The maximum number of 8255 digital i/o subdevices available on any ME-1400 device. */
+
+/**
+ * @brief The ME-1400 device class.
+ */
+typedef struct me1400_device {
+       me_device_t base;                                                                       /**< The Meilhaus device base class. */
+
+       spinlock_t clk_src_reg_lock;                                            /**< Guards the 8254 clock source registers. */
+       spinlock_t ctr_ctrl_reg_lock[ME1400_MAX_8254];          /**< Guards the 8254 ctrl registers. */
+
+       int dio_current_mode[ME1400_MAX_8255];                          /**< Saves the current mode setting of a single 8255 DIO chip. */
+       spinlock_t dio_ctrl_reg_lock[ME1400_MAX_8255];          /**< Guards the 8255 ctrl register and #dio_current_mode. */
+} me1400_device_t;
+
+/**
+ * @brief The ME-1400 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-1400 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me1400_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me1400_ext_irq.c b/drivers/staging/meilhaus/me1400_ext_irq.c
new file mode 100644 (file)
index 0000000..b8c2696
--- /dev/null
@@ -0,0 +1,517 @@
+/**
+ * @file me1400_ext_irq.c
+ *
+ * @brief ME-1400 external interrupt subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+#include "medebug.h"
+#include "meids.h"
+
+#include "me1400_ext_irq.h"
+#include "me1400_ext_irq_reg.h"
+
+/*
+ * Defines
+ */
+#define ME1400_EXT_IRQ_MAGIC_NUMBER    0x1401  /**< The magic number of the class structure. */
+#define ME1400_EXT_IRQ_NUMBER_CHANNELS 1       /**< One channel per counter. */
+
+/*
+ * Functions
+ */
+
+static int me1400_ext_irq_io_irq_start(struct me_subdevice *subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int irq_source,
+                                      int irq_edge, int irq_arg, int flags)
+{
+       me1400_ext_irq_subdevice_t *instance;
+       unsigned long cpu_flags;
+       uint8_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1400_ext_irq_subdevice_t *) subdevice;
+
+       if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
+               PERROR("Invalid irq source.\n");
+               return ME_ERRNO_INVALID_IRQ_SOURCE;
+       }
+
+       if (irq_edge != ME_IRQ_EDGE_RISING) {
+               PERROR("Invalid irq edge.\n");
+               return ME_ERRNO_INVALID_IRQ_EDGE;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       spin_lock(instance->clk_src_reg_lock);
+//                      // Enable IRQ on PLX
+//                      tmp = inb(instance->plx_intcs_reg) | (PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL | PLX_PCI_INT_EN);
+//                      outb(tmp, instance->plx_intcs_reg);
+//                      PDEBUG_REG("ctrl_reg outb(PLX:0x%lX)=0x%x\n", instance->plx_intcs_reg, tmp);
+
+       // Enable IRQ
+       switch (instance->device_id) {
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               tmp = inb(instance->ctrl_reg);
+               tmp |= ME1400CD_EXT_IRQ_CLK_EN;
+               outb(tmp, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, tmp);
+               break;
+
+       default:
+               outb(ME1400AB_EXT_IRQ_IRQ_EN, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base,
+                          ME1400AB_EXT_IRQ_IRQ_EN);
+               break;
+       }
+       spin_unlock(instance->clk_src_reg_lock);
+       instance->rised = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1400_ext_irq_io_irq_wait(struct me_subdevice *subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int *irq_count,
+                                     int *value, int time_out, int flags)
+{
+       me1400_ext_irq_subdevice_t *instance;
+       unsigned long cpu_flags;
+       long t = 0;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1400_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time out.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               /* Convert to ticks */
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->rised <= 0) {
+               instance->rised = 0;
+               if (time_out) {
+                       t = wait_event_interruptible_timeout(instance->
+                                                            wait_queue,
+                                                            (instance->rised !=
+                                                             0), t);
+
+                       if (t == 0) {
+                               PERROR("Wait on interrupt timed out.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               } else {
+                       wait_event_interruptible(instance->wait_queue,
+                                                (instance->rised != 0));
+               }
+
+               if (instance->rised < 0) {
+                       PERROR("Wait on interrupt aborted by user.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+       }
+
+       if (signal_pending(current)) {
+               PERROR("Wait on interrupt aborted by signal.\n");
+               err = ME_ERRNO_SIGNAL;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       instance->rised = 0;
+       *irq_count = instance->n;
+       *value = 1;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1400_ext_irq_io_irq_stop(struct me_subdevice *subdevice,
+                                     struct file *filep,
+                                     int channel, int flags)
+{
+       me1400_ext_irq_subdevice_t *instance;
+       unsigned long cpu_flags;
+       uint8_t tmp;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1400_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->clk_src_reg_lock);
+//                      // Disable IRQ on PLX
+//                      tmp = inb(instance->plx_intcs_reg) & ( ~(PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL | PLX_PCI_INT_EN));
+//                      outb(tmp, instance->plx_intcs_reg);
+//                      PDEBUG_REG("ctrl_reg outb(PLX:0x%lX)=0x%x\n", instance->plx_intcs_reg, tmp);
+
+       switch (instance->device_id) {
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               tmp = inb(instance->ctrl_reg);
+               tmp &= ~ME1400CD_EXT_IRQ_CLK_EN;
+               outb(tmp, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, tmp);
+
+               break;
+
+       default:
+               outb(0x00, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, 0x00);
+               break;
+       }
+       spin_unlock(instance->clk_src_reg_lock);
+       instance->rised = -1;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1400_ext_irq_io_reset_subdevice(struct me_subdevice *subdevice,
+                                            struct file *filep, int flags)
+{
+       me1400_ext_irq_subdevice_t *instance =
+           (me1400_ext_irq_subdevice_t *) subdevice;
+
+       PDEBUG("executed.\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       instance->n = 0;
+       return me1400_ext_irq_io_irq_stop(subdevice, filep, 0, flags);
+}
+
+static int me1400_ext_irq_query_number_channels(struct me_subdevice *subdevice,
+                                               int *number)
+{
+       PDEBUG("executed.\n");
+       *number = ME1400_EXT_IRQ_NUMBER_CHANNELS;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1400_ext_irq_query_subdevice_type(struct me_subdevice *subdevice,
+                                              int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_EXT_IRQ;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1400_ext_irq_query_subdevice_caps(struct me_subdevice *subdevice,
+                                              int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_EXT_IRQ_EDGE_RISING;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1400_ext_irq_query_subdevice_caps_args(struct me_subdevice
+                                                   *subdevice, int cap,
+                                                   int *args, int count)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me1400_ext_irq_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me1400_ext_irq_isr(int irq, void *dev_id,
+                                     struct pt_regs *regs)
+#endif
+{
+       me1400_ext_irq_subdevice_t *instance;
+       uint32_t status;
+       uint8_t tmp;
+
+       instance = (me1400_ext_irq_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       spin_lock(&instance->subdevice_lock);
+       status = inl(instance->plx_intcs_reg);
+//              if (!((status & PLX_LOCAL_INT1_STATE) && (status & PLX_LOCAL_INT1_EN) && (status & PLX_PCI_INT_EN)))
+       if ((status &
+            (PLX_LOCAL_INT1_STATE | PLX_LOCAL_INT1_EN | PLX_PCI_INT_EN)) !=
+           (PLX_LOCAL_INT1_STATE | PLX_LOCAL_INT1_EN | PLX_PCI_INT_EN)) {
+               spin_unlock(&instance->subdevice_lock);
+               PINFO("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n",
+                     jiffies, __FUNCTION__, status);
+               return IRQ_NONE;
+       }
+
+       inl(instance->ctrl_reg);
+
+       PDEBUG("executed.\n");
+
+       instance->n++;
+       instance->rised = 1;
+
+       switch (instance->device_id) {
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               spin_lock(instance->clk_src_reg_lock);
+               tmp = inb(instance->ctrl_reg);
+               tmp &= ~ME1400CD_EXT_IRQ_CLK_EN;
+               outb(tmp, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, tmp);
+               tmp |= ME1400CD_EXT_IRQ_CLK_EN;
+               outb(tmp, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, tmp);
+               spin_unlock(instance->clk_src_reg_lock);
+
+               break;
+
+       default:
+               outb(0, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, 0);
+               outb(ME1400AB_EXT_IRQ_IRQ_EN, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base,
+                          ME1400AB_EXT_IRQ_IRQ_EN);
+               break;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+static void me1400_ext_irq_destructor(struct me_subdevice *subdevice)
+{
+       me1400_ext_irq_subdevice_t *instance;
+       uint8_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1400_ext_irq_subdevice_t *) subdevice;
+
+       // Disable IRQ on PLX
+       tmp =
+           inb(instance->
+               plx_intcs_reg) & (~(PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL |
+                                   PLX_PCI_INT_EN));
+       outb(tmp, instance->plx_intcs_reg);
+       PDEBUG_REG("ctrl_reg outb(plx:0x%lX)=0x%x\n", instance->plx_intcs_reg,
+                  tmp);
+
+       free_irq(instance->irq, (void *)instance);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+me1400_ext_irq_subdevice_t *me1400_ext_irq_constructor(uint32_t device_id,
+                                                      uint32_t plx_reg_base,
+                                                      uint32_t me1400_reg_base,
+                                                      spinlock_t *
+                                                      clk_src_reg_lock,
+                                                      int irq)
+{
+       me1400_ext_irq_subdevice_t *subdevice;
+       int err;
+       uint8_t tmp;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me1400_ext_irq_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for 1400_ext_irq instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me1400_ext_irq_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+       subdevice->clk_src_reg_lock = clk_src_reg_lock;
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       subdevice->irq = irq;
+
+       err = request_irq(irq, me1400_ext_irq_isr,
+#ifdef IRQF_DISABLED
+                         IRQF_DISABLED | IRQF_SHARED,
+#else
+                         SA_INTERRUPT | SA_SHIRQ,
+#endif
+                         ME1400_NAME, (void *)subdevice);
+
+       if (err) {
+               PERROR("Can't get irq.\n");
+               me_subdevice_deinit(&subdevice->base);
+               kfree(subdevice);
+               return NULL;
+       }
+       PINFO("Registered irq=%d.\n", subdevice->irq);
+
+       /* Initialize registers */
+       subdevice->plx_intcs_reg = plx_reg_base + PLX_INTCSR_REG;
+       subdevice->ctrl_reg = me1400_reg_base + ME1400AB_EXT_IRQ_CTRL_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = me1400_reg_base;
+#endif
+
+       // Enable IRQ on PLX
+       tmp =
+           inb(subdevice->
+               plx_intcs_reg) | (PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL |
+                                 PLX_PCI_INT_EN);
+       outb(tmp, subdevice->plx_intcs_reg);
+       PDEBUG_REG("ctrl_reg outb(Pplx:0x%lX)=0x%x\n", subdevice->plx_intcs_reg,
+                  tmp);
+
+       /* Initialize the subdevice methods */
+       subdevice->base.me_subdevice_io_irq_start = me1400_ext_irq_io_irq_start;
+       subdevice->base.me_subdevice_io_irq_wait = me1400_ext_irq_io_irq_wait;
+       subdevice->base.me_subdevice_io_irq_stop = me1400_ext_irq_io_irq_stop;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me1400_ext_irq_io_reset_subdevice;
+       subdevice->base.me_subdevice_query_number_channels =
+           me1400_ext_irq_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me1400_ext_irq_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me1400_ext_irq_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_subdevice_caps_args =
+           me1400_ext_irq_query_subdevice_caps_args;
+       subdevice->base.me_subdevice_destructor = me1400_ext_irq_destructor;
+
+       subdevice->rised = 0;
+       subdevice->n = 0;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me1400_ext_irq.h b/drivers/staging/meilhaus/me1400_ext_irq.h
new file mode 100644 (file)
index 0000000..9b72a04
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * @file me1400_ext_irq.h
+ *
+ * @brief ME-1400 external interrupt implementation.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME1400_EXT_IRQ_H_
+#define _ME1400_EXT_IRQ_H_
+
+#include <linux/sched.h>
+
+#include "mesubdevice.h"
+#include "meslock.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The ME-1400 external interrupt subdevice class.
+ */
+typedef struct me1400_ext_irq_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *clk_src_reg_lock;   /**< Lock protecting the clock control register. */
+
+       wait_queue_head_t wait_queue;   /**< Queue to put on threads waiting for an interrupt. */
+
+       uint32_t device_id;                             /**< The device id of the device holding the subdevice. */
+       int irq;                                                /**< The irq number assigned by PCI BIOS. */
+       int rised;                                              /**< If true an interrupt has occured. */
+       unsigned int n;                                 /**< The number of interrupt since the driver was loaded. */
+
+       unsigned long plx_intcs_reg;    /**< The PLX interrupt control and status register. */
+       unsigned long ctrl_reg;                 /**< The control register. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me1400_ext_irq_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-1400 external interrupt instance.
+ *
+ * @param plx_reg_base The register base address of the PLX chip as returned by the PCI BIOS.
+ * @param me1400_reg_base The register base address of the ME-1400 device as returned by the PCI BIOS.
+ * @param irq The irq assigned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me1400_ext_irq_subdevice_t *me1400_ext_irq_constructor(uint32_t device_id,
+                                                      uint32_t plx_reg_base,
+                                                      uint32_t me1400_reg_base,
+                                                      spinlock_t *
+                                                      clk_src_reg_lock,
+                                                      int irq);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me1400_ext_irq_reg.h b/drivers/staging/meilhaus/me1400_ext_irq_reg.h
new file mode 100644 (file)
index 0000000..c9740f2
--- /dev/null
@@ -0,0 +1,56 @@
+/**
+ * @file me1400_ext_irq_reg.h
+ *
+ * @brief ME-1400 external interrupt register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1400_EXT_IRQ_REG_H_
+# define _ME1400_EXT_IRQ_REG_H_
+
+# ifdef __KERNEL__
+
+#  define PLX_INTCSR_REG                               0x4C    /**< The PLX interrupt control and status register offset. */
+#  define PLX_ICR_REG                                  0x50    /**< The PLX initialization control register offset. */
+
+#  define PLX_LOCAL_INT1_EN                            0x01    /**< If set the local interrupt 1 is enabled. */
+#  define PLX_LOCAL_INT1_POL                   0x02    /**< If set the local interrupt 1 polarity is high active. */
+#  define PLX_LOCAL_INT1_STATE                 0x04    /**< If set the local interrupt 1 is activ. */
+#  define PLX_LOCAL_INT2_EN                            0x08    /**< If set the local interrupt 2 is enabled. */
+#  define PLX_LOCAL_INT2_POL                   0x10    /**< If set the local interrupt 2 polarity is high active. */
+#  define PLX_LOCAL_INT2_STATE                 0x20    /**< If set the local interrupt 2 is activ. */
+#  define PLX_PCI_INT_EN                               0x40    /**< If set the PCI interrupt is enabled. */
+#  define PLX_SOFT_INT                                 0x80    /**< If set an interrupt is generated. */
+
+#  define ME1400AB_EXT_IRQ_CTRL_REG            0x11    /**< The external interrupt control register offset. */
+
+#  define ME1400AB_EXT_IRQ_CLK_EN              0x01    /**< If this bit is set, the clock output is enabled. */
+#  define ME1400AB_EXT_IRQ_IRQ_EN              0x02    /**< If set the external interrupt is enabled. Clearing this bit clears a pending interrupt. */
+
+#  define ME1400CD_EXT_IRQ_CTRL_REG            0x11    /**< The external interrupt control register offset. */
+
+#  define ME1400CD_EXT_IRQ_CLK_EN              0x10    /**< If set the external interrupt is enabled. Clearing this bit clears a pending interrupt.*/
+
+# endif        //__KERNEL__
+
+#endif //_ME1400_EXT_IRQ_REG_H_
diff --git a/drivers/staging/meilhaus/me1600_ao.c b/drivers/staging/meilhaus/me1600_ao.c
new file mode 100644 (file)
index 0000000..6f26665
--- /dev/null
@@ -0,0 +1,1033 @@
+/**
+ * @file me1600_ao.c
+ *
+ * @brief ME-1600 analog output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/* Includes
+ */
+
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+
+#include <linux/workqueue.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+#include "medebug.h"
+
+#include "me1600_ao_reg.h"
+#include "me1600_ao.h"
+
+/* Defines
+ */
+
+static void me1600_ao_destructor(struct me_subdevice *subdevice);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static void me1600_ao_work_control_task(void *subdevice);
+#else
+static void me1600_ao_work_control_task(struct work_struct *work);
+#endif
+
+static int me1600_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags);
+static int me1600_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep, int channel,
+                                     int single_config, int ref, int trig_chan,
+                                     int trig_type, int trig_edge, int flags);
+static int me1600_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep, int channel, int *value,
+                                   int time_out, int flags);
+static int me1600_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep, int channel, int value,
+                                    int time_out, int flags);
+static int me1600_ao_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number);
+static int me1600_ao_query_subdevice_type(me_subdevice_t * subdevice, int *type,
+                                         int *subtype);
+static int me1600_ao_query_subdevice_caps(me_subdevice_t * subdevice,
+                                         int *caps);
+static int me1600_ao_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit, int *min, int *max,
+                                           int *maxdata, int *range);
+static int me1600_ao_query_number_ranges(me_subdevice_t * subdevice, int unit,
+                                        int *count);
+static int me1600_ao_query_range_info(me_subdevice_t * subdevice, int range,
+                                     int *unit, int *min, int *max,
+                                     int *maxdata);
+
+/* Functions
+ */
+
+me1600_ao_subdevice_t *me1600_ao_constructor(uint32_t reg_base,
+                                            unsigned int ao_idx,
+                                            int curr,
+                                            spinlock_t * config_regs_lock,
+                                            spinlock_t * ao_shadows_lock,
+                                            me1600_ao_shadow_t *
+                                            ao_regs_shadows,
+                                            struct workqueue_struct *me1600_wq)
+{
+       me1600_ao_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed. idx=%d\n", ao_idx);
+
+       // Allocate memory for subdevice instance.
+       subdevice = kmalloc(sizeof(me1600_ao_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR
+                   ("Cannot get memory for analog output subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me1600_ao_subdevice_t));
+
+       // Initialize subdevice base class.
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+       subdevice->config_regs_lock = config_regs_lock;
+       subdevice->ao_shadows_lock = ao_shadows_lock;
+
+       // Save the subdevice index.
+       subdevice->ao_idx = ao_idx;
+
+       // Initialize range lists.
+       subdevice->u_ranges_count = 2;
+
+       subdevice->u_ranges[0].min = 0; //0V
+       subdevice->u_ranges[0].max = 9997558;   //10V
+
+       subdevice->u_ranges[1].min = -10E6;     //-10V
+       subdevice->u_ranges[1].max = 9995117;   //10V
+
+       if (curr) {             // This is version with current outputs.
+               subdevice->i_ranges_count = 2;
+
+               subdevice->i_ranges[0].min = 0; //0mA
+               subdevice->i_ranges[0].max = 19995117;  //20mA
+
+               subdevice->i_ranges[1].min = 4E3;       //4mA
+               subdevice->i_ranges[1].max = 19995118;  //20mA
+       } else {                // This is version without current outputs.
+               subdevice->i_ranges_count = 0;
+
+               subdevice->i_ranges[0].min = 0; //0mA
+               subdevice->i_ranges[0].max = 0; //0mA
+
+               subdevice->i_ranges[1].min = 0; //0mA
+               subdevice->i_ranges[1].max = 0; //0mA
+       }
+
+       // Initialize registers.
+       subdevice->uni_bi_reg = reg_base + ME1600_UNI_BI_REG;
+       subdevice->i_range_reg = reg_base + ME1600_020_420_REG;
+       subdevice->sim_output_reg = reg_base + ME1600_SIM_OUTPUT_REG;
+       subdevice->current_on_reg = reg_base + ME1600_CURRENT_ON_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       // Initialize shadow structure.
+       subdevice->ao_regs_shadows = ao_regs_shadows;
+
+       // Override base class methods.
+       subdevice->base.me_subdevice_destructor = me1600_ao_destructor;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me1600_ao_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me1600_ao_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me1600_ao_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me1600_ao_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me1600_ao_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me1600_ao_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me1600_ao_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_range_by_min_max =
+           me1600_ao_query_range_by_min_max;
+       subdevice->base.me_subdevice_query_number_ranges =
+           me1600_ao_query_number_ranges;
+       subdevice->base.me_subdevice_query_range_info =
+           me1600_ao_query_range_info;
+
+       // Initialize wait queue.
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       // Prepare work queue.
+       subdevice->me1600_workqueue = me1600_wq;
+
+/* workqueue API changed in kernel 2.6.20 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) )
+       INIT_WORK(&subdevice->ao_control_task, me1600_ao_work_control_task,
+                 (void *)subdevice);
+#else
+       INIT_DELAYED_WORK(&subdevice->ao_control_task,
+                         me1600_ao_work_control_task);
+#endif
+       return subdevice;
+}
+
+static void me1600_ao_destructor(struct me_subdevice *subdevice)
+{
+       me1600_ao_subdevice_t *instance;
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       instance->ao_control_task_flag = 0;
+
+       // Reset subdevice to asure clean exit.
+       me1600_ao_io_reset_subdevice(subdevice, NULL,
+                                    ME_IO_RESET_SUBDEVICE_NO_FLAGS);
+
+       // Remove any tasks from work queue. This is paranoic because it was done allready in reset().
+       if (!cancel_delayed_work(&instance->ao_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue.
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(2);
+       }
+}
+
+static int me1600_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags)
+{
+       me1600_ao_subdevice_t *instance;
+       uint16_t tmp;
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+       (instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx);     //Cancell waiting for trigger.
+
+       // Reset all settings.
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ao_shadows_lock);
+       (instance->ao_regs_shadows)->shadow[instance->ao_idx] = 0;
+       (instance->ao_regs_shadows)->mirror[instance->ao_idx] = 0;
+       (instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx);     //Not waiting for triggering.
+       (instance->ao_regs_shadows)->synchronous &= ~(0x1 << instance->ao_idx); //Individual triggering.
+
+       // Set output to default (safe) state.
+       spin_lock(instance->config_regs_lock);
+       tmp = inw(instance->uni_bi_reg);        // unipolar
+       tmp |= (0x1 << instance->ao_idx);
+       outw(tmp, instance->uni_bi_reg);
+       PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->uni_bi_reg - instance->reg_base, tmp);
+
+       tmp = inw(instance->current_on_reg);    // Volts only!
+       tmp &= ~(0x1 << instance->ao_idx);
+       tmp &= 0x00FF;
+       outw(tmp, instance->current_on_reg);
+       PDEBUG_REG("current_on_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->current_on_reg - instance->reg_base, tmp);
+
+       tmp = inw(instance->i_range_reg);       // 0..20mA <= If exists.
+       tmp &= ~(0x1 << instance->ao_idx);
+       outw(tmp, instance->i_range_reg);
+       PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->i_range_reg - instance->reg_base, tmp);
+
+       outw(0, (instance->ao_regs_shadows)->registry[instance->ao_idx]);
+       PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  (instance->ao_regs_shadows)->registry[instance->ao_idx] -
+                  instance->reg_base, 0);
+
+       // Trigger output.
+       outw(0x0000, instance->sim_output_reg);
+       PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->sim_output_reg - instance->reg_base, 0x0000);
+       outw(0xFFFF, instance->sim_output_reg);
+       PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->sim_output_reg - instance->reg_base, 0xFFFF);
+       spin_unlock(instance->config_regs_lock);
+       spin_unlock(instance->ao_shadows_lock);
+
+       // Set status to 'none'
+       instance->status = ao_status_none;
+       spin_unlock(&instance->subdevice_lock);
+
+       //Signal reset if user is on wait.
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me1600_ao_subdevice_t *instance;
+       uint16_t tmp;
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       // Checking parameters.
+       if (flags) {
+               PERROR
+                   ("Invalid flag specified. Must be ME_IO_SINGLE_CONFIG_NO_FLAGS.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (trig_edge != ME_TRIG_EDGE_NONE) {
+               PERROR
+                   ("Invalid trigger edge. Software trigger has not edge. Must be ME_TRIG_EDGE_NONE\n");
+               return ME_ERRNO_INVALID_TRIG_EDGE;
+       }
+
+       if (trig_type != ME_TRIG_TYPE_SW) {
+               PERROR("Invalid trigger edge. Must be ME_TRIG_TYPE_SW.\n");
+               return ME_ERRNO_INVALID_TRIG_TYPE;
+       }
+
+       if ((trig_chan != ME_TRIG_CHAN_DEFAULT)
+           && (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS)) {
+               PERROR("Invalid trigger channel specified.\n");
+               return ME_ERRNO_INVALID_TRIG_CHAN;
+       }
+
+       if (ref != ME_REF_AO_GROUND) {
+               PERROR
+                   ("Invalid reference. Analog outputs have to have got REF_AO_GROUND.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if (((single_config + 1) >
+            (instance->u_ranges_count + instance->i_ranges_count))
+           || (single_config < 0)) {
+               PERROR("Invalid range specified.\n");
+               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+       // Checking parameters - done. All is fine. Do config.
+
+       ME_SUBDEVICE_ENTER;
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ao_shadows_lock);
+       (instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx);     //Cancell waiting for trigger.
+       (instance->ao_regs_shadows)->shadow[instance->ao_idx] = 0;
+       (instance->ao_regs_shadows)->mirror[instance->ao_idx] = 0;
+
+       spin_lock(instance->config_regs_lock);
+       switch (single_config) {
+       case 0:         // 0V 10V
+               tmp = inw(instance->current_on_reg);    // Volts
+               tmp &= ~(0x1 << instance->ao_idx);
+               outw(tmp, instance->current_on_reg);
+               PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->current_on_reg - instance->reg_base, tmp);
+
+               // 0V
+               outw(0,
+                    (instance->ao_regs_shadows)->registry[instance->ao_idx]);
+               PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          (instance->ao_regs_shadows)->registry[instance->
+                                                                ao_idx] -
+                          instance->reg_base, 0);
+
+               tmp = inw(instance->uni_bi_reg);        // unipolar
+               tmp |= (0x1 << instance->ao_idx);
+               outw(tmp, instance->uni_bi_reg);
+               PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->uni_bi_reg - instance->reg_base, tmp);
+
+               tmp = inw(instance->i_range_reg);       // 0..20mA <= If exists.
+               tmp &= ~(0x1 << instance->ao_idx);
+               outw(tmp, instance->i_range_reg);
+               PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->i_range_reg - instance->reg_base, tmp);
+               break;
+
+       case 1:         // -10V 10V
+               tmp = inw(instance->current_on_reg);    // Volts
+               tmp &= ~(0x1 << instance->ao_idx);
+               outw(tmp, instance->current_on_reg);
+               PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->current_on_reg - instance->reg_base, tmp);
+
+               // 0V
+               outw(0x0800,
+                    (instance->ao_regs_shadows)->registry[instance->ao_idx]);
+               PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          (instance->ao_regs_shadows)->registry[instance->
+                                                                ao_idx] -
+                          instance->reg_base, 0x0800);
+
+               tmp = inw(instance->uni_bi_reg);        // bipolar
+               tmp &= ~(0x1 << instance->ao_idx);
+               outw(tmp, instance->uni_bi_reg);
+               PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->uni_bi_reg - instance->reg_base, tmp);
+
+               tmp = inw(instance->i_range_reg);       // 0..20mA <= If exists.
+               tmp &= ~(0x1 << instance->ao_idx);
+               outw(tmp, instance->i_range_reg);
+               PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->i_range_reg - instance->reg_base, tmp);
+               break;
+
+       case 2:         // 0mA 20mA
+               tmp = inw(instance->current_on_reg);    // mAmpers
+               tmp |= (0x1 << instance->ao_idx);
+               outw(tmp, instance->current_on_reg);
+               PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->current_on_reg - instance->reg_base, tmp);
+
+               tmp = inw(instance->i_range_reg);       // 0..20mA
+               tmp &= ~(0x1 << instance->ao_idx);
+               outw(tmp, instance->i_range_reg);
+               PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->i_range_reg - instance->reg_base, tmp);
+
+               // 0mA
+               outw(0,
+                    (instance->ao_regs_shadows)->registry[instance->ao_idx]);
+               PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          (instance->ao_regs_shadows)->registry[instance->
+                                                                ao_idx] -
+                          instance->reg_base, 0);
+
+               tmp = inw(instance->uni_bi_reg);        // unipolar
+               tmp |= (0x1 << instance->ao_idx);
+               outw(tmp, instance->uni_bi_reg);
+               PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->uni_bi_reg - instance->reg_base, tmp);
+               break;
+
+       case 3:         // 4mA 20mA
+               tmp = inw(instance->current_on_reg);    // mAmpers
+               tmp |= (0x1 << instance->ao_idx);
+               outw(tmp, instance->current_on_reg);
+               PDEBUG_REG("current_on_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->current_on_reg - instance->reg_base, tmp);
+
+               tmp = inw(instance->i_range_reg);       // 4..20mA
+               tmp |= (0x1 << instance->ao_idx);
+               outw(tmp, instance->i_range_reg);
+               PDEBUG_REG("i_range_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->i_range_reg - instance->reg_base, tmp);
+
+               // 4mA
+               outw(0,
+                    (instance->ao_regs_shadows)->registry[instance->ao_idx]);
+               PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          (instance->ao_regs_shadows)->registry[instance->
+                                                                ao_idx] -
+                          instance->reg_base, 0);
+
+               tmp = inw(instance->uni_bi_reg);        // unipolar
+               tmp |= (0x1 << instance->ao_idx);
+               outw(tmp, instance->uni_bi_reg);
+               PDEBUG_REG("uni_bi_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->uni_bi_reg - instance->reg_base, tmp);
+               break;
+       }
+
+       // Trigger output.
+       outw(0x0000, instance->sim_output_reg);
+       PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->sim_output_reg - instance->reg_base, 0x0000);
+       outw(0xFFFF, instance->sim_output_reg);
+       PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->sim_output_reg - instance->reg_base, 0xFFFF);
+
+       if (trig_chan == ME_TRIG_CHAN_DEFAULT) {        // Individual triggering.
+               (instance->ao_regs_shadows)->synchronous &=
+                   ~(0x1 << instance->ao_idx);
+               PDEBUG("Individual triggering.\n");
+       } else if (trig_chan == ME_TRIG_CHAN_SYNCHRONOUS) {     // Synchronous triggering.
+               (instance->ao_regs_shadows)->synchronous |=
+                   (0x1 << instance->ao_idx);
+               PDEBUG("Synchronous triggering.\n");
+       }
+       spin_unlock(instance->config_regs_lock);
+       spin_unlock(instance->ao_shadows_lock);
+
+       instance->status = ao_status_single_configured;
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me1600_ao_subdevice_t *instance;
+       unsigned long delay = 0;
+       unsigned long j = 0;
+       int err = ME_ERRNO_SUCCESS;
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags & ~ME_IO_SINGLE_NONBLOCKING) {
+               PERROR("Invalid flag specified. %d\n", flags);
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if ((!flags) && ((instance->ao_regs_shadows)->trigger & instance->ao_idx)) {    //Blocking mode. Wait for software trigger.
+               if (time_out) {
+                       delay = (time_out * HZ) / 1000;
+                       if (delay == 0)
+                               delay = 1;
+               }
+
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (!((instance->
+                                                    ao_regs_shadows)->
+                                                   trigger & instance->
+                                                   ao_idx)),
+                                                (delay) ? delay : LONG_MAX);
+
+               if (instance == ao_status_none) {       // Reset was called.
+                       PDEBUG("Single canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - j) >= delay)) {
+                       PDEBUG("Timeout reached.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               }
+       }
+
+       *value = (instance->ao_regs_shadows)->mirror[instance->ao_idx];
+
+       return err;
+}
+
+static int me1600_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me1600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long delay = 0;
+       int i;
+       unsigned long j = 0;
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags &
+           ~(ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS |
+             ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (value & ~ME1600_AO_MAX_DATA) {
+               PERROR("Invalid value provided.\n");
+               return ME_ERRNO_VALUE_OUT_OF_RANGE;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+       (instance->ao_regs_shadows)->trigger &= ~(0x1 << instance->ao_idx);     //Cancell waiting for trigger.
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+       //Write value.
+       spin_lock(instance->ao_shadows_lock);
+       (instance->ao_regs_shadows)->shadow[instance->ao_idx] =
+           (uint16_t) value;
+
+       if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       // Trigger all outputs from synchronous list.
+               for (i = 0; i < (instance->ao_regs_shadows)->count; i++) {
+                       if (((instance->ao_regs_shadows)->synchronous & (0x1 << i)) || (i == instance->ao_idx)) {       // Set all from synchronous list to correct state.
+                               PDEBUG
+                                   ("Synchronous triggering: output %d. idx=%d\n",
+                                    i, instance->ao_idx);
+                               (instance->ao_regs_shadows)->mirror[i] =
+                                   (instance->ao_regs_shadows)->shadow[i];
+
+                               outw((instance->ao_regs_shadows)->shadow[i],
+                                    (instance->ao_regs_shadows)->registry[i]);
+                               PDEBUG_REG
+                                   ("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    (instance->ao_regs_shadows)->registry[i] -
+                                    instance->reg_base,
+                                    (instance->ao_regs_shadows)->shadow[i]);
+
+                               (instance->ao_regs_shadows)->trigger &=
+                                   ~(0x1 << i);
+                       }
+               }
+
+               // Trigger output.
+               outw(0x0000, instance->sim_output_reg);
+               PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->sim_output_reg - instance->reg_base, 0);
+               outw(0xFFFF, instance->sim_output_reg);
+               PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->sim_output_reg - instance->reg_base,
+                          0xFFFF);
+               instance->status = ao_status_single_end;
+       } else {                // Individual mode.
+               if ((instance->ao_regs_shadows)->synchronous & (0x1 << instance->ao_idx)) {     // Put on synchronous start list. Set output as waiting for trigger.
+                       PDEBUG("Add to synchronous list. idx=%d\n",
+                              instance->ao_idx);
+                       (instance->ao_regs_shadows)->trigger |=
+                           (0x1 << instance->ao_idx);
+                       instance->status = ao_status_single_run;
+                       PDEBUG("Synchronous list: 0x%x.\n",
+                              (instance->ao_regs_shadows)->synchronous);
+               } else {        // Fired this one.
+                       PDEBUG("Triggering. idx=%d\n", instance->ao_idx);
+                       (instance->ao_regs_shadows)->mirror[instance->ao_idx] =
+                           (instance->ao_regs_shadows)->shadow[instance->
+                                                               ao_idx];
+
+                       outw((instance->ao_regs_shadows)->
+                            shadow[instance->ao_idx],
+                            (instance->ao_regs_shadows)->registry[instance->
+                                                                  ao_idx]);
+                       PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  (instance->ao_regs_shadows)->
+                                  registry[instance->ao_idx] -
+                                  instance->reg_base,
+                                  (instance->ao_regs_shadows)->
+                                  shadow[instance->ao_idx]);
+
+                       // Set output as triggered.
+                       (instance->ao_regs_shadows)->trigger &=
+                           ~(0x1 << instance->ao_idx);
+
+                       // Trigger output.
+                       outw(0x0000, instance->sim_output_reg);
+                       PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->sim_output_reg -
+                                  instance->reg_base, 0);
+                       outw(0xFFFF, instance->sim_output_reg);
+                       PDEBUG_REG("sim_output_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->sim_output_reg -
+                                  instance->reg_base, 0xFFFF);
+                       instance->status = ao_status_single_end;
+               }
+       }
+       spin_unlock(instance->ao_shadows_lock);
+
+       //Init control task
+       instance->timeout.delay = delay;
+       instance->timeout.start_time = jiffies;
+       instance->ao_control_task_flag = 1;
+       queue_delayed_work(instance->me1600_workqueue,
+                          &instance->ao_control_task, 1);
+
+       if ((!flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING) && ((instance->ao_regs_shadows)->trigger & instance->ao_idx)) {      //Blocking mode. Wait for software trigger.
+               if (time_out) {
+                       delay = (time_out * HZ) / 1000;
+                       if (delay == 0)
+                               delay = 1;
+               }
+
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (!((instance->
+                                                    ao_regs_shadows)->
+                                                   trigger & instance->
+                                                   ao_idx)),
+                                                (delay) ? delay : LONG_MAX);
+
+               if (instance == ao_status_none) {
+                       PDEBUG("Single canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - j) >= delay)) {
+                       PDEBUG("Timeout reached.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1600_ao_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       me1600_ao_subdevice_t *instance;
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *number = 1;            //Every subdevice has only 1 channel.
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_query_subdevice_type(me_subdevice_t * subdevice, int *type,
+                                         int *subtype)
+{
+       me1600_ao_subdevice_t *instance;
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *type = ME_TYPE_AO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_AO_TRIG_SYNCHRONOUS;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range)
+{
+       me1600_ao_subdevice_t *instance;
+       int i;
+       int r = -1;
+       int diff = 21E6;
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if ((*max - *min) < 0) {
+               PERROR("Invalid minimum and maximum values specified.\n");
+               return ME_ERRNO_INVALID_MIN_MAX;
+       }
+       // Maximum ranges are slightly less then 10V or 20mA. For convenient we accepted this value as valid one.
+       if (unit == ME_UNIT_VOLT) {
+               for (i = 0; i < instance->u_ranges_count; i++) {
+                       if ((instance->u_ranges[i].min <= *min)
+                           && ((instance->u_ranges[i].max + 5000) >= *max)) {
+                               if ((instance->u_ranges[i].max -
+                                    instance->u_ranges[i].min) - (*max -
+                                                                  *min) <
+                                   diff) {
+                                       r = i;
+                                       diff =
+                                           (instance->u_ranges[i].max -
+                                            instance->u_ranges[i].min) -
+                                           (*max - *min);
+                               }
+                       }
+               }
+
+               if (r < 0) {
+                       PERROR("No matching range found.\n");
+                       return ME_ERRNO_NO_RANGE;
+               } else {
+                       *min = instance->u_ranges[r].min;
+                       *max = instance->u_ranges[r].max;
+                       *range = r;
+               }
+       } else if (unit == ME_UNIT_AMPERE) {
+               for (i = 0; i < instance->i_ranges_count; i++) {
+                       if ((instance->i_ranges[i].min <= *min)
+                           && (instance->i_ranges[i].max + 5000 >= *max)) {
+                               if ((instance->i_ranges[i].max -
+                                    instance->i_ranges[i].min) - (*max -
+                                                                  *min) <
+                                   diff) {
+                                       r = i;
+                                       diff =
+                                           (instance->i_ranges[i].max -
+                                            instance->i_ranges[i].min) -
+                                           (*max - *min);
+                               }
+                       }
+               }
+
+               if (r < 0) {
+                       PERROR("No matching range found.\n");
+                       return ME_ERRNO_NO_RANGE;
+               } else {
+                       *min = instance->i_ranges[r].min;
+                       *max = instance->i_ranges[r].max;
+                       *range = r + instance->u_ranges_count;
+               }
+       } else {
+               PERROR("Invalid physical unit specified.\n");
+               return ME_ERRNO_INVALID_UNIT;
+       }
+       *maxdata = ME1600_AO_MAX_DATA;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count)
+{
+       me1600_ao_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+       switch (unit) {
+       case ME_UNIT_VOLT:
+               *count = instance->u_ranges_count;
+               break;
+       case ME_UNIT_AMPERE:
+               *count = instance->i_ranges_count;
+               break;
+       case ME_UNIT_ANY:
+               *count = instance->u_ranges_count + instance->i_ranges_count;
+               break;
+       default:
+               *count = 0;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1600_ao_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata)
+{
+       me1600_ao_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me1600_ao_subdevice_t *) subdevice;
+
+       if (((range + 1) >
+            (instance->u_ranges_count + instance->i_ranges_count))
+           || (range < 0)) {
+               PERROR("Invalid range number specified.\n");
+               return ME_ERRNO_INVALID_RANGE;
+       }
+
+       if (range < instance->u_ranges_count) {
+               *unit = ME_UNIT_VOLT;
+               *min = instance->u_ranges[range].min;
+               *max = instance->u_ranges[range].max;
+       } else if (range < instance->u_ranges_count + instance->i_ranges_count) {
+               *unit = ME_UNIT_AMPERE;
+               *min = instance->i_ranges[range - instance->u_ranges_count].min;
+               *max = instance->i_ranges[range - instance->u_ranges_count].max;
+       }
+       *maxdata = ME1600_AO_MAX_DATA;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static void me1600_ao_work_control_task(void *subdevice)
+#else
+static void me1600_ao_work_control_task(struct work_struct *work)
+#endif
+{
+       me1600_ao_subdevice_t *instance;
+       int reschedule = 1;
+       int signaling = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       instance = (me1600_ao_subdevice_t *) subdevice;
+#else
+       instance =
+           container_of((void *)work, me1600_ao_subdevice_t, ao_control_task);
+#endif
+
+       PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies,
+             instance->ao_idx);
+
+       if (!((instance->ao_regs_shadows)->trigger & instance->ao_idx)) {       // Output was triggerd.
+               // Signal the end.
+               signaling = 1;
+               reschedule = 0;
+               if (instance->status == ao_status_single_run) {
+                       instance->status = ao_status_single_end;
+               }
+
+       } else if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {        // Timeout
+               PDEBUG("Timeout reached.\n");
+               spin_lock(instance->ao_shadows_lock);
+               // Restore old settings.
+               PDEBUG("Write old value back to register.\n");
+               (instance->ao_regs_shadows)->shadow[instance->ao_idx] =
+                   (instance->ao_regs_shadows)->mirror[instance->ao_idx];
+
+               outw((instance->ao_regs_shadows)->mirror[instance->ao_idx],
+                    (instance->ao_regs_shadows)->registry[instance->ao_idx]);
+               PDEBUG_REG("channel_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          (instance->ao_regs_shadows)->registry[instance->
+                                                                ao_idx] -
+                          instance->reg_base,
+                          (instance->ao_regs_shadows)->mirror[instance->
+                                                              ao_idx]);
+
+               //Remove from synchronous strt list.
+               (instance->ao_regs_shadows)->trigger &=
+                   ~(0x1 << instance->ao_idx);
+               if (instance->status == ao_status_none) {
+                       instance->status = ao_status_single_end;
+               }
+               spin_unlock(instance->ao_shadows_lock);
+
+               // Signal the end.
+               signaling = 1;
+               reschedule = 0;
+       }
+
+       if (signaling) {        //Signal it.
+               wake_up_interruptible_all(&instance->wait_queue);
+       }
+
+       if (instance->ao_control_task_flag && reschedule) {     // Reschedule task
+               queue_delayed_work(instance->me1600_workqueue,
+                                  &instance->ao_control_task, 1);
+       } else {
+               PINFO("<%s> Ending control task.\n", __FUNCTION__);
+       }
+
+}
diff --git a/drivers/staging/meilhaus/me1600_ao.h b/drivers/staging/meilhaus/me1600_ao.h
new file mode 100644 (file)
index 0000000..b82bf5a
--- /dev/null
@@ -0,0 +1,132 @@
+/**
+ * @file me1600_ao.h
+ *
+ * @brief Meilhaus ME-1600 analog output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1600_AO_H_
+#define _ME1600_AO_H_
+
+# include <linux/version.h>
+# include "mesubdevice.h"
+
+# ifdef __KERNEL__
+
+#  define ME1600_MAX_RANGES    2       /**< Specifies the maximum number of ranges in me1600_ao_subdevice_t::u_ranges und me1600_ao_subdevice_t::i_ranges. */
+
+/**
+ * @brief Defines a entry in the range table.
+ */
+typedef struct me1600_ao_range_entry {
+       int32_t min;
+       int32_t max;
+} me1600_ao_range_entry_t;
+
+typedef struct me1600_ao_timeout {
+       unsigned long start_time;
+       unsigned long delay;
+} me1600_ao_timeout_t;
+
+typedef struct me1600_ao_shadow {
+       int count;
+       unsigned long *registry;
+       uint16_t *shadow;
+       uint16_t *mirror;
+       uint16_t synchronous;                                                                   /**< Synchronization list. */
+       uint16_t trigger;                                                                               /**< Synchronization flag. */
+} me1600_ao_shadow_t;
+
+typedef enum ME1600_AO_STATUS {
+       ao_status_none = 0,
+       ao_status_single_configured,
+       ao_status_single_run,
+       ao_status_single_end,
+       ao_status_last
+} ME1600_AO_STATUS;
+
+/**
+ * @brief The ME-1600 analog output subdevice class.
+ */
+typedef struct me1600_ao_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                                                                    /**< The subdevice base class. */
+
+       /* Attributes */
+       int ao_idx;                                                                                             /**< The index of the analog output subdevice on the device. */
+
+       spinlock_t subdevice_lock;                                                              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *config_regs_lock;                                                   /**< Spin lock to protect configuration registers from concurrent access. */
+
+       int u_ranges_count;                                                                             /**< The number of voltage ranges available on this subdevice. */
+       me1600_ao_range_entry_t u_ranges[ME1600_MAX_RANGES];    /**< Array holding the voltage ranges on this subdevice. */
+       int i_ranges_count;                                                                             /**< The number of current ranges available on this subdevice. */
+       me1600_ao_range_entry_t i_ranges[ME1600_MAX_RANGES];    /**< Array holding the current ranges on this subdevice. */
+
+       /* Registers */
+       unsigned long uni_bi_reg;                                                               /**< Register for switching between unipoar and bipolar output mode. */
+       unsigned long i_range_reg;                                                              /**< Register for switching between ranges. */
+       unsigned long sim_output_reg;                                                   /**< Register used in order to update all channels simultaneously. */
+       unsigned long current_on_reg;                                                   /**< Register enabling current output on the fourth subdevice. */
+#   ifdef PDEBUG_REG
+       unsigned long reg_base;
+#   endif
+
+       ME1600_AO_STATUS status;
+       me1600_ao_shadow_t *ao_regs_shadows;                                    /**< Addresses and shadows of output's registers. */
+       spinlock_t *ao_shadows_lock;                                                    /**< Protects the shadow's struct. */
+       int mode;                                                                                               /**< Mode in witch output should works. */
+       wait_queue_head_t wait_queue;                                                   /**< Wait queue to put on tasks waiting for data to arrive. */
+       me1600_ao_timeout_t timeout;                                                    /**< The timeout for start in blocking and non-blocking mode. */
+       struct workqueue_struct *me1600_workqueue;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       struct work_struct ao_control_task;
+#else
+       struct delayed_work ao_control_task;
+#endif
+
+       volatile int ao_control_task_flag;                                              /**< Flag controling reexecuting of control task */
+} me1600_ao_subdevice_t;
+
+/**
+ * @brief The constructor to generate a subdevice template instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param ao_idx The index of the analog output subdevice on the device.
+ * @param current Flag indicating that analog output with #ao_idx of 3 is capable of current output.
+ * @param config_regs_lock Pointer to spin lock protecting the configuration registers and from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me1600_ao_subdevice_t *me1600_ao_constructor(uint32_t reg_base,
+                                            unsigned int ao_idx,
+                                            int curr,
+                                            spinlock_t * config_regs_lock,
+                                            spinlock_t * ao_shadows_lock,
+                                            me1600_ao_shadow_t *
+                                            ao_regs_shadows,
+                                            struct workqueue_struct
+                                            *me1600_wq);
+
+# endif        //__KERNEL__
+#endif //_ME1600_AO_H_
diff --git a/drivers/staging/meilhaus/me1600_ao_reg.h b/drivers/staging/meilhaus/me1600_ao_reg.h
new file mode 100644 (file)
index 0000000..31e7800
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * @file me1600_ao_reg.h
+ *
+ * @brief ME-1600 analog output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1600_AO_REG_H_
+#define _ME1600_AO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME1600_CHANNEL_0_REG    0x00   /**< Register to set a digital value on channel 0. */
+#define ME1600_CHANNEL_1_REG    0x02   /**< Register to set a digital value on channel 1. */
+#define ME1600_CHANNEL_2_REG    0x04   /**< Register to set a digital value on channel 2. */
+#define ME1600_CHANNEL_3_REG    0x06   /**< Register to set a digital value on channel 3. */
+#define ME1600_CHANNEL_4_REG    0x08   /**< Register to set a digital value on channel 4. */
+#define ME1600_CHANNEL_5_REG    0x0A   /**< Register to set a digital value on channel 5. */
+#define ME1600_CHANNEL_6_REG    0x0C   /**< Register to set a digital value on channel 6. */
+#define ME1600_CHANNEL_7_REG    0x0E   /**< Register to set a digital value on channel 7. */
+#define ME1600_CHANNEL_8_REG    0x10   /**< Register to set a digital value on channel 8. */
+#define ME1600_CHANNEL_9_REG    0x12   /**< Register to set a digital value on channel 9. */
+#define ME1600_CHANNEL_10_REG   0x14   /**< Register to set a digital value on channel 10. */
+#define ME1600_CHANNEL_11_REG   0x16   /**< Register to set a digital value on channel 11. */
+#define ME1600_CHANNEL_12_REG   0x18   /**< Register to set a digital value on channel 12. */
+#define ME1600_CHANNEL_13_REG   0x1A   /**< Register to set a digital value on channel 13. */
+#define ME1600_CHANNEL_14_REG   0x1C   /**< Register to set a digital value on channel 14. */
+#define ME1600_CHANNEL_15_REG   0x1E   /**< Register to set a digital value on channel 15. */
+
+/* Every channel one bit: bipolar = 0, unipolar = 1 */
+#define ME1600_UNI_BI_REG              0x20    /**< Register to switch between unipolar and bipolar. */
+
+/* Every channel one bit (only lower 8 Bits): 0..20mA = 0, 4..20mA = 1 */
+#define ME1600_020_420_REG             0x22    /**< Register to switch between the two current ranges. */
+
+/* If a bit is set, the corresponding DAC (4 ports each) is
+   not set at the moment you write to an output of it.
+   Clearing the bit updates the port. */
+#define ME1600_SIM_OUTPUT_REG  0x24    /**< Register to update all channels of a subdevice simultaneously. */
+
+/* Current on/off (only lower 8 bits): off = 0, on  = 1 */
+#define ME1600_CURRENT_ON_REG  0x26    /**< Register to swicht between voltage and current output. */
+
+#define ME1600_AO_MAX_DATA             0x0FFF  /**< The maximum digital data accepted by an analog output channel. */
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me1600_device.c b/drivers/staging/meilhaus/me1600_device.c
new file mode 100644 (file)
index 0000000..3bc2cb1
--- /dev/null
@@ -0,0 +1,261 @@
+/**
+ * @file me1600_device.c
+ *
+ * @brief ME-1600 device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "mesubdevice.h"
+#include "me1600_device.h"
+
+static void me1600_set_registry(me1600_device_t * subdevice, uint32_t reg_base);
+static void me1600_destructor(struct me_device *device);
+
+/**
+ * @brief Global variable.
+ * This is working queue for runing a separate atask that will be responsible for work status (start, stop, timeouts).
+ */
+static struct workqueue_struct *me1600_workqueue;
+
+me_device_t *me1600_pci_constructor(struct pci_dev *pci_device)
+{
+       int err;
+       me1600_device_t *me1600_device;
+       me_subdevice_t *subdevice;
+       unsigned int chip_idx;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me1600_device = kmalloc(sizeof(me1600_device_t), GFP_KERNEL);
+
+       if (!me1600_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(me1600_device, 0, sizeof(me1600_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me1600_device, pci_device);
+
+       if (err) {
+               kfree(me1600_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+       // Initialize spin lock .
+       spin_lock_init(&me1600_device->config_regs_lock);
+       spin_lock_init(&me1600_device->ao_shadows_lock);
+
+       // Get the number of analog output subdevices.
+       chip_idx =
+           me1600_versions_get_device_index(me1600_device->base.info.pci.
+                                            device_id);
+
+       // Create shadow instance.
+       me1600_device->ao_regs_shadows.count =
+           me1600_versions[chip_idx].ao_chips;
+       me1600_device->ao_regs_shadows.registry =
+           kmalloc(me1600_versions[chip_idx].ao_chips * sizeof(unsigned long),
+                   GFP_KERNEL);
+       me1600_set_registry(me1600_device,
+                           me1600_device->base.info.pci.reg_bases[2]);
+       me1600_device->ao_regs_shadows.shadow =
+           kmalloc(me1600_versions[chip_idx].ao_chips * sizeof(uint16_t),
+                   GFP_KERNEL);
+       me1600_device->ao_regs_shadows.mirror =
+           kmalloc(me1600_versions[chip_idx].ao_chips * sizeof(uint16_t),
+                   GFP_KERNEL);
+
+       // Create subdevice instances.
+       for (i = 0; i < me1600_versions[chip_idx].ao_chips; i++) {
+               subdevice =
+                   (me_subdevice_t *) me1600_ao_constructor(me1600_device->
+                                                            base.info.pci.
+                                                            reg_bases[2], i,
+                                                            ((me1600_versions
+                                                              [chip_idx].curr >
+                                                              i) ? 1 : 0),
+                                                            &me1600_device->
+                                                            config_regs_lock,
+                                                            &me1600_device->
+                                                            ao_shadows_lock,
+                                                            &me1600_device->
+                                                            ao_regs_shadows,
+                                                            me1600_workqueue);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me1600_device);
+                       kfree(me1600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me1600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Overwrite base class methods.
+       me1600_device->base.me_device_destructor = me1600_destructor;
+
+       return (me_device_t *) me1600_device;
+}
+
+static void me1600_destructor(struct me_device *device)
+{
+       me1600_device_t *me1600_device = (me1600_device_t *) device;
+       PDEBUG("executed.\n");
+
+       // Destroy shadow instance.
+       kfree(me1600_device->ao_regs_shadows.registry);
+       kfree(me1600_device->ao_regs_shadows.shadow);
+       kfree(me1600_device->ao_regs_shadows.mirror);
+
+       me_device_deinit((me_device_t *) me1600_device);
+       kfree(me1600_device);
+}
+
+static void me1600_set_registry(me1600_device_t * subdevice, uint32_t reg_base)
+{                              // Create shadow structure.
+       if (subdevice->ao_regs_shadows.count >= 1) {
+               subdevice->ao_regs_shadows.registry[0] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_0_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 2) {
+               subdevice->ao_regs_shadows.registry[1] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_1_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 3) {
+               subdevice->ao_regs_shadows.registry[2] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_2_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 4) {
+               subdevice->ao_regs_shadows.registry[3] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_3_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 5) {
+               subdevice->ao_regs_shadows.registry[4] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_4_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 6) {
+               subdevice->ao_regs_shadows.registry[5] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_5_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 7) {
+               subdevice->ao_regs_shadows.registry[6] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_6_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 8) {
+               subdevice->ao_regs_shadows.registry[7] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_7_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 9) {
+               subdevice->ao_regs_shadows.registry[8] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_8_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 10) {
+               subdevice->ao_regs_shadows.registry[9] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_9_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 11) {
+               subdevice->ao_regs_shadows.registry[10] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_10_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 12) {
+               subdevice->ao_regs_shadows.registry[11] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_11_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 13) {
+               subdevice->ao_regs_shadows.registry[12] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_12_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 14) {
+               subdevice->ao_regs_shadows.registry[13] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_13_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 15) {
+               subdevice->ao_regs_shadows.registry[14] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_14_REG);
+       }
+       if (subdevice->ao_regs_shadows.count >= 16) {
+               subdevice->ao_regs_shadows.registry[15] =
+                   (unsigned long)(reg_base + ME1600_CHANNEL_15_REG);
+       }
+       if (subdevice->ao_regs_shadows.count > 16) {
+               PERROR("More than 16 outputs! (%d)\n",
+                      subdevice->ao_regs_shadows.count);
+       }
+}
+
+// Init and exit of module.
+
+static int __init me1600_init(void)
+{
+       PDEBUG("executed\n.");
+
+       me1600_workqueue = create_singlethread_workqueue("me1600");
+       return 0;
+}
+
+static void __exit me1600_exit(void)
+{
+       PDEBUG("executed\n.");
+
+       flush_workqueue(me1600_workqueue);
+       destroy_workqueue(me1600_workqueue);
+}
+
+module_init(me1600_init);
+module_exit(me1600_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for ME-1600 Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-1600 Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me1600_pci_constructor);
diff --git a/drivers/staging/meilhaus/me1600_device.h b/drivers/staging/meilhaus/me1600_device.h
new file mode 100644 (file)
index 0000000..f7b231f
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * @file me1600_device.h
+ *
+ * @brief ME-1600 device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME1600_H
+#define _ME1600_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+#include "me1600_ao.h"
+#include "me1600_ao_reg.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure to store device capabilities.
+ */
+typedef struct me1600_version {
+       uint16_t device_id;                             /**< The PCI device id of the device. */
+       unsigned int ao_chips;                  /**< The number of analog outputs on the device. */
+       int curr;                                               /**< Flag to identify amounts of current output. */
+} me1600_version_t;
+
+/**
+  * @brief Defines for each ME-1600 device version its capabilities.
+ */
+static me1600_version_t me1600_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME1600_4U, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME1600_8U, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME1600_12U, 12, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME1600_16U, 16, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I, 16, 8},
+       {0}
+};
+
+/**< Returns the number of entries in #me1600_versions. */
+#define ME1600_DEVICE_VERSIONS (sizeof(me1600_versions) / sizeof(me1600_version_t) - 1)
+
+/**
+ * @brief Returns the index of the device entry in #me1600_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me1600_versions.
+ */
+static inline unsigned int me1600_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME1600_DEVICE_VERSIONS; i++)
+               if (me1600_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-1600 device class structure.
+ */
+typedef struct me1600_device {
+       me_device_t base;                                               /**< The Meilhaus device base class. */
+       spinlock_t config_regs_lock;                    /**< Protects the configuration registers. */
+
+       me1600_ao_shadow_t ao_regs_shadows;             /**< Addresses and shadows of output's registers. */
+       spinlock_t ao_shadows_lock;                             /**< Protects the shadow's struct. */
+} me1600_device_t;
+
+/**
+ * @brief The ME-1600 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-1600 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me1600_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_ai.c b/drivers/staging/meilhaus/me4600_ai.c
new file mode 100644 (file)
index 0000000..1a0de5d
--- /dev/null
@@ -0,0 +1,3434 @@
+/**
+ * @file me4600_ai.c
+ *
+ * @brief ME-4000 analog input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+# define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+#include "medebug.h"
+#include "meids.h"
+
+#include "me4600_reg.h"
+#include "me4600_ai_reg.h"
+#include "me4600_ai.h"
+
+/*
+ * Declarations (local)
+ */
+
+static void me4600_ai_destructor(struct me_subdevice *subdevice);
+static int me4600_ai_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags);
+
+static int me4600_ai_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags);
+
+static int me4600_ai_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags);
+
+static int me4600_ai_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags);
+static int me4600_ai_io_stream_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int read_mode,
+                                   int *values, int *count, int flags);
+static int me4600_ai_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags);
+static int inline me4600_ai_io_stream_read_get_value(me4600_ai_subdevice_t *
+                                                    instance, int *values,
+                                                    const int count,
+                                                    const int flags);
+
+static int me4600_ai_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags);
+static int me4600_ai_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags);
+static int me4600_ai_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags);
+
+static int me4600_ai_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range);
+static int me4600_ai_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count);
+static int me4600_ai_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata);
+static int me4600_ai_query_timer(me_subdevice_t * subdevice,
+                                int timer,
+                                int *base_frequency,
+                                long long *min_ticks, long long *max_ticks);
+static int me4600_ai_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number);
+static int me4600_ai_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype);
+static int me4600_ai_query_subdevice_caps(me_subdevice_t * subdevice,
+                                         int *caps);
+static int me4600_ai_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                              int cap, int *args, int count);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me4600_ai_isr(int irq, void *dev_id);
+#else
+static irqreturn_t me4600_ai_isr(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+
+static int ai_mux_toggler(me4600_ai_subdevice_t * subdevice);
+
+/** Immidiate stop.
+* Reset all IRQ's sources. (block laches)
+* Preserve FIFO
+*/
+static int ai_stop_immediately(me4600_ai_subdevice_t * instance);
+
+/** Immidiate stop.
+* Reset all IRQ's sources. (block laches)
+* Reset data FIFO
+*/
+void inline ai_stop_isr(me4600_ai_subdevice_t * instance);
+
+/** Interrupt logics.
+* Read datas
+* Reset latches
+*/
+void ai_limited_isr(me4600_ai_subdevice_t * instance, const uint32_t irq_status,
+                   const uint32_t ctrl_status);
+void ai_infinite_isr(me4600_ai_subdevice_t * instance,
+                    const uint32_t irq_status, const uint32_t ctrl_status);
+
+/** Last chunck of datas. We must reschedule sample counter.
+* Leaving SC_RELOAD doesn't do any harm, but in some bad case can make extra interrupts.
+* When threshold is wrongly set some IRQ are lost.(!!!)
+*/
+void inline ai_reschedule_SC(me4600_ai_subdevice_t * instance);
+
+/** Read datas from FIFO and copy them to buffer */
+static int inline ai_read_data(me4600_ai_subdevice_t * instance,
+                              const int count);
+
+/** Copy rest of data from fifo to circular buffer.*/
+static int inline ai_read_data_pooling(me4600_ai_subdevice_t * instance);
+
+/** Set ISM to next state for infinite data aqusation mode*/
+void inline ai_infinite_ISM(me4600_ai_subdevice_t * instance);
+
+/** Set ISM to next state for define amount of data aqusation mode*/
+void inline ai_limited_ISM(me4600_ai_subdevice_t * instance,
+                          uint32_t irq_status);
+
+/** Set ISM to next stage for limited mode */
+void inline ai_data_acquisition_logic(me4600_ai_subdevice_t * instance);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static void me4600_ai_work_control_task(void *subdevice);
+#else
+static void me4600_ai_work_control_task(struct work_struct *work);
+#endif
+
+/* Definitions
+ */
+
+me4600_ai_subdevice_t *me4600_ai_constructor(uint32_t reg_base,
+                                            unsigned int channels,
+                                            unsigned int ranges,
+                                            int isolated,
+                                            int sh,
+                                            int irq,
+                                            spinlock_t * ctrl_reg_lock,
+                                            struct workqueue_struct *me4600_wq)
+{
+       me4600_ai_subdevice_t *subdevice;
+       int err;
+       unsigned int i;
+
+       PDEBUG("executed. idx=0\n");
+
+       // Allocate memory for subdevice instance.
+       subdevice = kmalloc(sizeof(me4600_ai_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_ai_subdevice_t));
+
+       // Initialize subdevice base class.
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       // Initialize circular buffer.
+       subdevice->circ_buf.mask = ME4600_AI_CIRC_BUF_COUNT - 1;
+
+       subdevice->circ_buf.buf =
+           (void *)__get_free_pages(GFP_KERNEL, ME4600_AI_CIRC_BUF_SIZE_ORDER);
+       PDEBUG("circ_buf = %p size=%ld\n", subdevice->circ_buf.buf,
+              ME4600_AI_CIRC_BUF_SIZE);
+
+       if (!subdevice->circ_buf.buf) {
+               PERROR("Cannot get circular buffer.\n");
+               me_subdevice_deinit((me_subdevice_t *) subdevice);
+               kfree(subdevice);
+               return NULL;
+       }
+
+       memset(subdevice->circ_buf.buf, 0, ME4600_AI_CIRC_BUF_SIZE);
+       subdevice->circ_buf.head = 0;
+       subdevice->circ_buf.tail = 0;
+       subdevice->status = ai_status_none;
+
+       // Initialize wait queue.
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       // Save the number of channels.
+       subdevice->channels = channels;
+
+       /* Initialize the single config entries to reset values */
+       for (i = 0; i < channels; i++) {
+               subdevice->single_config[i].status = ME_SINGLE_CHANNEL_NOT_CONFIGURED;  //not configured
+       }
+
+       // Save if isolated device.
+       subdevice->isolated = isolated;
+
+       // Save if sample and hold is available.
+       subdevice->sh = sh;
+
+       // Set stream config to not configured state.
+       subdevice->fifo_irq_threshold = 0;
+       subdevice->data_required = 0;
+       subdevice->chan_list_len = 0;
+
+       // Initialize registers addresses.
+       subdevice->ctrl_reg = reg_base + ME4600_AI_CTRL_REG;
+       subdevice->status_reg = reg_base + ME4600_AI_STATUS_REG;
+       subdevice->channel_list_reg = reg_base + ME4600_AI_CHANNEL_LIST_REG;
+       subdevice->data_reg = reg_base + ME4600_AI_DATA_REG;
+       subdevice->chan_timer_reg = reg_base + ME4600_AI_CHAN_TIMER_REG;
+       subdevice->chan_pre_timer_reg = reg_base + ME4600_AI_CHAN_PRE_TIMER_REG;
+       subdevice->scan_timer_low_reg = reg_base + ME4600_AI_SCAN_TIMER_LOW_REG;
+       subdevice->scan_timer_high_reg =
+           reg_base + ME4600_AI_SCAN_TIMER_HIGH_REG;
+       subdevice->scan_pre_timer_low_reg =
+           reg_base + ME4600_AI_SCAN_PRE_TIMER_LOW_REG;
+       subdevice->scan_pre_timer_high_reg =
+           reg_base + ME4600_AI_SCAN_PRE_TIMER_HIGH_REG;
+       subdevice->start_reg = reg_base + ME4600_AI_START_REG;
+       subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
+       subdevice->sample_counter_reg = reg_base + ME4600_AI_SAMPLE_COUNTER_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       // Initialize ranges.
+       subdevice->ranges_len = ranges;
+       subdevice->ranges[0].min = -10E6;
+       subdevice->ranges[0].max = 9999694;
+
+       subdevice->ranges[1].min = 0;
+       subdevice->ranges[1].max = 9999847;
+
+       subdevice->ranges[2].min = -25E5;
+       subdevice->ranges[2].max = 2499923;
+
+       subdevice->ranges[3].min = 0;
+       subdevice->ranges[3].max = 2499961;
+
+       // We have to switch the mux in order to get it work correctly.
+       ai_mux_toggler(subdevice);
+
+       // Register interrupt service routine.
+       subdevice->irq = irq;
+       if (request_irq(subdevice->irq, me4600_ai_isr,
+#ifdef IRQF_DISABLED
+                       IRQF_DISABLED | IRQF_SHARED,
+#else
+                       SA_INTERRUPT | SA_SHIRQ,
+#endif
+                       ME4600_NAME, subdevice)) {
+               PERROR("Cannot register interrupt service routine.\n");
+               me_subdevice_deinit((me_subdevice_t *) subdevice);
+               free_pages((unsigned long)subdevice->circ_buf.buf,
+                          ME4600_AI_CIRC_BUF_SIZE_ORDER);
+               subdevice->circ_buf.buf = NULL;
+               kfree(subdevice);
+               return NULL;
+       }
+       PINFO("Registered irq=%d.\n", subdevice->irq);
+
+       // Override base class methods.
+       subdevice->base.me_subdevice_destructor = me4600_ai_destructor;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_ai_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me4600_ai_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me4600_ai_io_single_read;
+       subdevice->base.me_subdevice_io_stream_config =
+           me4600_ai_io_stream_config;
+       subdevice->base.me_subdevice_io_stream_new_values =
+           me4600_ai_io_stream_new_values;
+       subdevice->base.me_subdevice_io_stream_read = me4600_ai_io_stream_read;
+       subdevice->base.me_subdevice_io_stream_start =
+           me4600_ai_io_stream_start;
+       subdevice->base.me_subdevice_io_stream_status =
+           me4600_ai_io_stream_status;
+       subdevice->base.me_subdevice_io_stream_stop = me4600_ai_io_stream_stop;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_ai_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_ai_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_ai_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_subdevice_caps_args =
+           me4600_ai_query_subdevice_caps_args;
+       subdevice->base.me_subdevice_query_range_by_min_max =
+           me4600_ai_query_range_by_min_max;
+       subdevice->base.me_subdevice_query_number_ranges =
+           me4600_ai_query_number_ranges;
+       subdevice->base.me_subdevice_query_range_info =
+           me4600_ai_query_range_info;
+       subdevice->base.me_subdevice_query_timer = me4600_ai_query_timer;
+
+       // Prepare work queue.
+       subdevice->me4600_workqueue = me4600_wq;
+
+/* workqueue API changed in kernel 2.6.20 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) )
+       INIT_WORK(&subdevice->ai_control_task, me4600_ai_work_control_task,
+                 (void *)subdevice);
+#else
+       INIT_DELAYED_WORK(&subdevice->ai_control_task,
+                         me4600_ai_work_control_task);
+#endif
+
+       return subdevice;
+}
+
+static void me4600_ai_destructor(struct me_subdevice *subdevice)
+{
+       me4600_ai_subdevice_t *instance;
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance->ai_control_task_flag = 0;
+       // Reset subdevice to asure clean exit.
+       me4600_ai_io_reset_subdevice(subdevice, NULL,
+                                    ME_IO_RESET_SUBDEVICE_NO_FLAGS);
+
+       // Remove any tasks from work queue. This is paranoic because it was done allready in reset().
+       if (!cancel_delayed_work(&instance->ai_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue.
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(2);
+       }
+
+       free_irq(instance->irq, instance);
+       free_pages((unsigned long)instance->circ_buf.buf,
+                  ME4600_AI_CIRC_BUF_SIZE_ORDER);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+static int me4600_ai_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       volatile uint32_t ctrl;
+       unsigned long status;
+       const int timeout = HZ / 10;    //100ms
+       int i;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       instance->ai_control_task_flag = 0;
+       instance->status = ai_status_none;
+
+       for (i = 0; i <= timeout; i++) {
+               spin_lock_irqsave(instance->ctrl_reg_lock, status);
+               ctrl = inl(instance->ctrl_reg);
+               //Stop DMA
+               ctrl &= ~ME4600_AI_CTRL_RPCI_FIFO;
+               // Stop all actions. No conditions!
+               ctrl &= ~ME4600_AI_CTRL_BIT_STOP;
+               ctrl |= ME4600_AI_CTRL_BIT_IMMEDIATE_STOP;
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               spin_unlock_irqrestore(instance->ctrl_reg_lock, status);
+
+               if (!(inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM))
+                       break;
+
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+       }
+
+       if (i > timeout) {
+               PERROR("FSM is still busy.\n");
+               ME_SUBDEVICE_EXIT;
+               return ME_ERRNO_INTERNAL;
+       }
+
+       spin_lock_irqsave(instance->ctrl_reg_lock, status);
+       ctrl = inl(instance->ctrl_reg);
+       // Clear all features. Dissable interrupts.
+       ctrl &= ~(ME4600_AI_CTRL_BIT_STOP
+                 | ME4600_AI_CTRL_BIT_LE_IRQ
+                 | ME4600_AI_CTRL_BIT_HF_IRQ | ME4600_AI_CTRL_BIT_SC_IRQ);
+       ctrl |= (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP
+                | ME4600_AI_CTRL_BIT_LE_IRQ_RESET
+                | ME4600_AI_CTRL_BIT_HF_IRQ_RESET
+                | ME4600_AI_CTRL_BIT_SC_IRQ_RESET);
+
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, status);
+
+       outl(ME4600_AI_MIN_CHAN_TICKS - 1, instance->chan_timer_reg);
+       PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%llx\n",
+                  instance->reg_base,
+                  instance->chan_timer_reg - instance->reg_base,
+                  ME4600_AI_MIN_CHAN_TICKS);
+       outl(ME4600_AI_MIN_ACQ_TICKS - 1, instance->chan_pre_timer_reg);
+       PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%llx\n",
+                  instance->reg_base,
+                  instance->chan_pre_timer_reg - instance->reg_base,
+                  ME4600_AI_MIN_ACQ_TICKS);
+       outl(0, instance->scan_timer_low_reg);
+       PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_timer_low_reg - instance->reg_base, 0);
+       outl(0, instance->scan_timer_high_reg);
+       PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_timer_high_reg - instance->reg_base, 0);
+       outl(0, instance->scan_pre_timer_low_reg);
+       PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_low_reg - instance->reg_base, 0);
+       outl(0, instance->scan_pre_timer_high_reg);
+       PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_high_reg - instance->reg_base, 0);
+       outl(0xEFFFFFFF, instance->sample_counter_reg);
+       PDEBUG_REG("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->sample_counter_reg - instance->reg_base,
+                  0xEFFFFFFF);
+
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+
+       instance->fifo_irq_threshold = 0;
+       instance->data_required = 0;
+       instance->chan_list_len = 0;
+
+       // Initialize the single config entries to reset values.
+       for (i = 0; i < instance->channels; i++) {
+               instance->single_config[i].status =
+                   ME_SINGLE_CHANNEL_NOT_CONFIGURED;
+       }
+       instance->status = ai_status_none;
+
+       //Signal reset if user is on wait.
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ai_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags;
+       int i;
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (flags & ~ME_IO_SINGLE_CONFIG_CONTINUE) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       switch (trig_type) {
+       case ME_TRIG_TYPE_SW:
+               if (trig_edge != ME_TRIG_EDGE_NONE) {
+                       PERROR
+                           ("Invalid trigger edge. Software trigger has not edge.\n");
+                       return ME_ERRNO_INVALID_TRIG_EDGE;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               if (instance->channels <= 16)   //Only versions with 32 channels have analog trigger (4670 and 4680)
+               {
+                       PERROR("Invalid trigger type specified.\n");
+                       return ME_ERRNO_INVALID_TRIG_TYPE;
+               }
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               if ((trig_edge != ME_TRIG_EDGE_ANY)
+                   && (trig_edge != ME_TRIG_EDGE_RISING)
+                   && (trig_edge != ME_TRIG_EDGE_FALLING)) {
+                       PERROR("Invalid trigger edge specified.\n");
+                       return ME_ERRNO_INVALID_TRIG_EDGE;
+               }
+               break;
+
+       default:
+               PERROR("Invalid trigger type specified.\n");
+               return ME_ERRNO_INVALID_TRIG_TYPE;
+       }
+
+       if (trig_chan != ME_TRIG_CHAN_DEFAULT) {
+               PERROR("Invalid trigger channel specified.\n");
+               return ME_ERRNO_INVALID_TRIG_CHAN;
+       }
+
+       if ((single_config < 0) || (single_config >= instance->ranges_len)) {
+               PERROR("Invalid single config specified.\n");
+               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+       }
+
+       if ((ref != ME_REF_AI_GROUND) && (ref != ME_REF_AI_DIFFERENTIAL)) {
+               PERROR("Invalid analog reference specified.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if ((single_config % 2) && (ref != ME_REF_AI_GROUND)) {
+               PERROR("Invalid analog reference specified.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if ((ref == ME_REF_AI_DIFFERENTIAL)
+           && ((instance->channels == 16) || (channel >= 16))) {
+               PERROR("Invalid analog reference specified.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if (channel < 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (channel >= instance->channels) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       //Prepare data entry.
+       // Common for all modes.
+       instance->single_config[channel].entry =
+           channel | ME4600_AI_LIST_LAST_ENTRY;
+
+       if (ref == ME_REF_AI_DIFFERENTIAL) {    // ME_REF_AI_DIFFERENTIAL
+               instance->single_config[channel].entry |=
+                   ME4600_AI_LIST_INPUT_DIFFERENTIAL;
+       }
+/*
+               // ME4600_AI_LIST_INPUT_SINGLE_ENDED = 0x0000
+               // 'entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED' <== Do nothing. Removed.
+               else
+               {// ME_REF_AI_GROUND
+                       instance->single_config[channel].entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED;
+               }
+*/
+       switch (single_config) {
+       case 0:         //-10V..10V
+/*
+                                       // ME4600_AI_LIST_RANGE_BIPOLAR_10 = 0x0000
+                                       // 'entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10' <== Do nothing. Removed.
+                                       instance->single_config[channel].entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10;
+*/ break;
+
+       case 1:         //0V..10V
+               instance->single_config[channel].entry |=
+                   ME4600_AI_LIST_RANGE_UNIPOLAR_10;
+               break;
+
+       case 2:         //-2.5V..2.5V
+               instance->single_config[channel].entry |=
+                   ME4600_AI_LIST_RANGE_BIPOLAR_2_5;
+               break;
+
+       case 3:         //0V..2.5V
+               instance->single_config[channel].entry |=
+                   ME4600_AI_LIST_RANGE_UNIPOLAR_2_5;
+               break;
+       }
+
+       // Prepare control register.
+       // Common for all modes.
+       instance->single_config[channel].ctrl =
+           ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO;
+
+       switch (trig_type) {
+       case ME_TRIG_TYPE_SW:
+               // Nothing to set.
+               break;
+
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               instance->single_config[channel].ctrl |=
+                   ME4600_AI_CTRL_BIT_EX_TRIG_ANALOG;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               instance->single_config[channel].ctrl |=
+                   ME4600_AI_CTRL_BIT_EX_TRIG;
+               break;
+       }
+
+       switch (trig_edge) {
+       case ME_TRIG_EDGE_RISING:
+               // Nothing to set.
+               break;
+
+       case ME_TRIG_EDGE_ANY:
+               instance->single_config[channel].ctrl |=
+                   ME4600_AI_CTRL_BIT_EX_TRIG_BOTH;
+
+       case ME_TRIG_EDGE_FALLING:
+               instance->single_config[channel].ctrl |=
+                   ME4600_AI_CTRL_BIT_EX_TRIG_FALLING;
+               break;
+       }
+
+       // Enable this channel
+       instance->single_config[channel].status = ME_SINGLE_CHANNEL_CONFIGURED;
+
+       // Copy this settings to other outputs.
+       if (flags == ME_IO_SINGLE_CONFIG_CONTINUE) {
+               for (i = channel + 1; i < instance->channels; i++) {
+                       instance->single_config[i].ctrl =
+                           instance->single_config[channel].ctrl;
+                       instance->single_config[i].entry =
+                           instance->single_config[channel].entry;
+                       instance->single_config[i].status =
+                           ME_SINGLE_CHANNEL_CONFIGURED;
+               }
+       }
+
+       instance->status = ai_status_single_configured;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ai_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       volatile uint32_t tmp;
+       volatile uint32_t val;
+       unsigned long cpu_flags;
+       int err = ME_ERRNO_SUCCESS;
+
+       unsigned long j;
+       unsigned long delay = 0;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (instance->status != ai_status_single_configured) {
+               PERROR("Subdevice not configured to work in single mode!\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       if ((channel > instance->channels) || (channel < 0)) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (instance->single_config[channel].status !=
+           ME_SINGLE_CHANNEL_CONFIGURED) {
+               PERROR("Channel is not configured to work in single mode!\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       if (inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM) {
+               PERROR("Subdevice is busy.\n");
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       // Cancel control task
+       PDEBUG("Cancel control task.\n");
+       instance->ai_control_task_flag = 0;
+       cancel_delayed_work(&instance->ai_control_task);
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       // Mark that StreamConfig is removed.
+       instance->chan_list_len = 0;
+
+       spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+       /// @note Imprtant: Preserve EXT IRQ settings.
+       tmp = inl(instance->ctrl_reg);
+       // Clear FIFOs and dissable interrupts
+       tmp &=
+           ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO);
+
+       tmp &=
+           ~(ME4600_AI_CTRL_BIT_SC_IRQ | ME4600_AI_CTRL_BIT_HF_IRQ |
+             ME4600_AI_CTRL_BIT_LE_IRQ);
+       tmp |=
+           ME4600_AI_CTRL_BIT_SC_IRQ_RESET | ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
+           ME4600_AI_CTRL_BIT_LE_IRQ_RESET;
+
+       tmp |= ME4600_AI_CTRL_BIT_IMMEDIATE_STOP;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       outl(0, instance->scan_pre_timer_low_reg);
+       PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_low_reg - instance->reg_base, 0);
+       outl(0, instance->scan_pre_timer_high_reg);
+       PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_high_reg - instance->reg_base, 0);
+       outl(0, instance->scan_timer_low_reg);
+       PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_timer_low_reg - instance->reg_base, 0);
+       outl(0, instance->scan_timer_high_reg);
+       PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_timer_high_reg - instance->reg_base, 0);
+       outl(65, instance->chan_timer_reg);
+       PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->chan_timer_reg - instance->reg_base, 65);
+       outl(65, instance->chan_pre_timer_reg);
+       PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->chan_pre_timer_reg - instance->reg_base, 65);
+
+       //Reactive FIFOs. Enable work.
+       tmp |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       outl(instance->single_config[channel].entry,
+            instance->channel_list_reg);
+       PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->channel_list_reg - instance->reg_base,
+                  instance->single_config[channel].entry);
+
+       // Preserve EXT IRQ settings.
+       tmp &= (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
+       outl(instance->single_config[channel].ctrl | tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base,
+                  instance->single_config[channel].ctrl | tmp);
+
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+       if (!(instance->single_config[channel].ctrl & ME4600_AI_CTRL_BIT_EX_TRIG)) {    // Software start
+               inl(instance->start_reg);
+               PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base,
+                          instance->start_reg - instance->reg_base);
+
+               delay = 2;
+       }
+
+       j = jiffies;
+
+       while (!(inl(instance->status_reg) & ME4600_AI_STATUS_BIT_EF_DATA)) {
+               if (delay && ((jiffies - j) >= delay)) {
+                       if (!(instance->single_config[channel].ctrl & ME4600_AI_CTRL_BIT_EX_TRIG)) {    // Software start.
+                               PERROR("Value not available after wait.\n");
+                               err = ME_ERRNO_INTERNAL;
+                       } else {        // External start.
+                               PERROR("Timeout reached.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+                       break;
+               }
+               // Wait
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+
+               if (signal_pending(current)) {
+                       PERROR
+                           ("Wait on external trigger interrupted by signal.\n");
+                       err = ME_ERRNO_SIGNAL;
+                       break;
+               }
+
+               if (instance->status != ai_status_single_configured) {
+                       PERROR("Wait interrupted by reset.\n");
+                       err = ME_ERRNO_CANCELLED;
+                       break;
+               }
+       }
+
+       // Read value.
+       if (!err) {
+               val = inl(instance->data_reg) ^ 0x8000;
+               PDEBUG_REG("data_reg inl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->data_reg - instance->reg_base, val);
+               *value = val & ME4600_AI_MAX_DATA;
+       } else {
+               *value = 0xFFFFFFFF;
+       }
+
+       // Restore settings.
+       spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+       tmp = inl(instance->ctrl_reg);
+       // Clear FIFOs and dissable interrupts.
+       tmp &=
+           ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO);
+       tmp |= ME4600_AI_CTRL_BIT_SC_IRQ | ME4600_AI_CTRL_BIT_HF_IRQ;
+       tmp |=
+           ME4600_AI_CTRL_BIT_SC_IRQ_RESET | ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
+           ME4600_AI_CTRL_BIT_LE_IRQ_RESET | ME4600_AI_CTRL_BIT_IMMEDIATE_STOP;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ai_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       int i;                  // internal multipurpose variable
+       unsigned long long data_required;
+
+       volatile uint32_t entry;
+       volatile uint32_t ctrl = ME4600_AI_CTRL_BIT_IMMEDIATE_STOP;
+       volatile uint32_t tmp;  // use when current copy of register's value needed
+       unsigned long cpu_flags;
+
+       uint64_t acq_ticks;
+       uint64_t scan_ticks;
+       uint64_t conv_ticks;
+       unsigned int acq_start_ticks_low = trigger->iAcqStartTicksLow;
+       unsigned int acq_start_ticks_high = trigger->iAcqStartTicksHigh;
+       unsigned int scan_start_ticks_low = trigger->iScanStartTicksLow;
+       unsigned int scan_start_ticks_high = trigger->iScanStartTicksHigh;
+       unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow;
+       unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER
+           // Convert ticks to 64 bit long values
+           acq_ticks =
+           (uint64_t) acq_start_ticks_low +
+           ((uint64_t) acq_start_ticks_high << 32);
+       scan_ticks =
+           (uint64_t) scan_start_ticks_low +
+           ((uint64_t) scan_start_ticks_high << 32);
+       conv_ticks =
+           (uint64_t) conv_start_ticks_low +
+           ((uint64_t) conv_start_ticks_high << 32);
+
+       // Check settings - begin
+       switch (trigger->iAcqStartTrigType) {
+       case ME_TRIG_TYPE_SW:
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               break;
+
+       default:
+               PERROR("Invalid acquisition start trigger type specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;
+               goto ERROR;
+               break;
+       }
+
+       if ((trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW)
+           && (trigger->iAcqStartTrigEdge != ME_TRIG_EDGE_NONE)) {
+               PERROR("Invalid acquisition start trigger edge specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+               goto ERROR;
+       }
+
+       if (trigger->iAcqStartTrigType != ME_TRIG_TYPE_SW) {
+               switch (trigger->iAcqStartTrigEdge) {
+               case ME_TRIG_EDGE_RISING:
+               case ME_TRIG_EDGE_FALLING:
+               case ME_TRIG_EDGE_ANY:
+                       break;
+
+               default:
+                       PERROR
+                           ("Invalid acquisition start trigger edge specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+                       goto ERROR;
+                       break;
+               }
+       }
+
+       if (trigger->iAcqStartTrigChan != ME_TRIG_CHAN_DEFAULT) {
+               PERROR
+                   ("Invalid acquisition start trigger channel specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN;
+               goto ERROR;
+       }
+
+       if ((acq_ticks < ME4600_AI_MIN_ACQ_TICKS)
+           || (acq_ticks > ME4600_AI_MAX_ACQ_TICKS)) {
+               PERROR
+                   ("Invalid acquisition start trigger argument specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_START_ARG;
+               goto ERROR;
+       }
+
+       switch (trigger->iScanStartTrigType) {
+
+       case ME_TRIG_TYPE_TIMER:
+               if ((scan_ticks < ME4600_AI_MIN_SCAN_TICKS)
+                   || (scan_ticks > ME4600_AI_MAX_SCAN_TICKS)
+                   || (scan_ticks < count * conv_ticks)
+                   ) {
+                       PERROR("Invalid scan start argument specified.\n");
+                       err = ME_ERRNO_INVALID_SCAN_START_ARG;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               if (trigger->iAcqStartTrigType != ME_TRIG_TYPE_EXT_DIGITAL) {
+                       PERROR
+                           ("Invalid scan start trigger type specified (Acq is HW digital)\n");
+                       err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               if (trigger->iAcqStartTrigType != ME_TRIG_TYPE_EXT_ANALOG) {
+                       PERROR
+                           ("Invalid scan start trigger type specified (Acq is HW analog)\n");
+                       err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_FOLLOW:
+               break;
+
+       default:
+               PERROR("Invalid scan start trigger type specified.\n");
+               err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+               goto ERROR;
+               break;
+       }
+
+       switch (trigger->iConvStartTrigType) {
+
+       case ME_TRIG_TYPE_TIMER:
+               if ((conv_ticks < ME4600_AI_MIN_CHAN_TICKS)
+                   || (conv_ticks > ME4600_AI_MAX_CHAN_TICKS)) {
+                       PERROR
+                           ("Invalid conv start trigger argument specified.\n");
+                       err = ME_ERRNO_INVALID_CONV_START_ARG;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               if ((trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW)
+                   || (trigger->iAcqStartTrigType !=
+                       ME_TRIG_TYPE_EXT_DIGITAL)) {
+                       PERROR("Invalid conv start trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               if ((trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW)
+                   || (trigger->iAcqStartTrigType !=
+                       ME_TRIG_TYPE_EXT_ANALOG)) {
+                       PERROR("Invalid conv start trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+                       goto ERROR;
+               }
+               break;
+
+       default:
+               PERROR("Invalid conv start trigger type specified.\n");
+               err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+               goto ERROR;
+
+               break;
+       }
+/**
+* Aceptable settings:
+* iScanStopTrigType            :       iAcqStopTrigType
+*
+* ME_TRIG_TYPE_NONE            :       ME_TRIG_TYPE_NONE       -> infinite count with manual stop
+* ME_TRIG_TYPE_NONE            :       ME_TRIG_TYPE_COUNT      -> stop after getting iScanStopCount list of values (iScanStopCount * count)
+* ME_TRIG_TYPE_COUNT   :       ME_TRIG_TYPE_FOLLOW     -> stop after getting iAcqStopCount values (it can stops in midle of the list)
+*/
+       switch (trigger->iScanStopTrigType) {
+
+       case ME_TRIG_TYPE_NONE:
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (trigger->iScanStopCount <= 0) {
+                       PERROR("Invalid scan stop argument specified.\n");
+                       err = ME_ERRNO_INVALID_SCAN_STOP_ARG;
+                       goto ERROR;
+               }
+               break;
+
+       default:
+               PERROR("Invalid scan stop trigger type specified.\n");
+               err = ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE;
+               goto ERROR;
+               break;
+       }
+
+       switch (trigger->iAcqStopTrigType) {
+
+       case ME_TRIG_TYPE_NONE:
+               if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
+                       PERROR("Invalid acq stop trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_FOLLOW:
+               if (trigger->iScanStopTrigType != ME_TRIG_TYPE_COUNT) {
+                       PERROR("Invalid acq stop trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+                       goto ERROR;
+               }
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
+                       PERROR("Invalid acq stop trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+                       goto ERROR;
+               }
+
+               if (trigger->iAcqStopCount <= 0) {
+                       PERROR
+                           ("Invalid acquisition or scan stop argument specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_ARG;
+                       goto ERROR;
+               }
+               break;
+
+       default:
+               PERROR("Invalid acq stop trigger type specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+               goto ERROR;
+               break;
+       }
+
+       if ((count <= 0) || (count > ME4600_AI_LIST_COUNT)) {
+               PERROR("Invalid channel list count specified.\n");
+               err = ME_ERRNO_INVALID_CONFIG_LIST_COUNT;
+               goto ERROR;
+       }
+///This is general limitation
+//      if (fifo_irq_threshold < 0 || fifo_irq_threshold >= ME4600_AI_CIRC_BUF_COUNT)
+///This is limitation from Windows. I use it for compatibility.
+       if (fifo_irq_threshold < 0
+           || fifo_irq_threshold >= ME4600_AI_FIFO_COUNT) {
+               PERROR("Invalid fifo irq threshold specified.\n");
+               err = ME_ERRNO_INVALID_FIFO_IRQ_THRESHOLD;
+               goto ERROR;
+       }
+
+       if ((config_list[0].iRef == ME_REF_AI_DIFFERENTIAL)
+           && (instance->channels == 16)) {
+               PERROR
+                   ("Differential reference is not available on this subdevice.\n");
+               err = ME_ERRNO_INVALID_REF;
+               goto ERROR;
+       }
+
+       if (flags & ME_IO_STREAM_CONFIG_SAMPLE_AND_HOLD) {
+               if (!instance->sh) {
+                       PERROR
+                           ("Sample and hold is not available for this board.\n");
+                       err = ME_ERRNO_INVALID_FLAGS;
+                       goto ERROR;
+               }
+               if (config_list[0].iRef == ME_REF_AI_DIFFERENTIAL) {
+                       PERROR
+                           ("Sample and hold is not available in differential mode.\n");
+                       err = ME_ERRNO_INVALID_FLAGS;
+                       goto ERROR;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               if ((config_list[i].iStreamConfig < 0)
+                   || (config_list[i].iStreamConfig >= instance->ranges_len)) {
+                       PERROR("Invalid stream config specified.\n");
+                       err = ME_ERRNO_INVALID_STREAM_CONFIG;
+                       goto ERROR;
+               }
+
+               if ((config_list[i].iRef != ME_REF_AI_GROUND)
+                   && (config_list[i].iRef != ME_REF_AI_DIFFERENTIAL)) {
+                       PERROR("Invalid references in the list. Ref=0x%x\n",
+                              config_list[i].iRef);
+                       err = ME_ERRNO_INVALID_REF;
+                       goto ERROR;
+               }
+
+               if (config_list[i].iStreamConfig % 2) { // StreamConfig: 1 or 3
+                       if (config_list[i].iRef == ME_REF_AI_DIFFERENTIAL) {
+                               PERROR
+                                   ("Only bipolar modes support differential measurement.\n");
+                               err = ME_ERRNO_INVALID_REF;
+                               goto ERROR;
+                       }
+               }
+
+               if (config_list[i].iRef != config_list[0].iRef) {
+                       PERROR
+                           ("Not all references in the configuration list are equal. Ref[0]=0x%x Ref[%d]=0x%x\n",
+                            config_list[0].iRef, i, config_list[i].iRef);
+                       err = ME_ERRNO_INVALID_REF;
+                       goto ERROR;
+               }
+
+               if ((config_list[i].iRef == ME_REF_AI_DIFFERENTIAL)
+                   && (config_list[i].iChannel >= 16)) {
+                       PERROR("Channel not available in differential mode.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+                       goto ERROR;
+               }
+
+               if ((config_list[i].iChannel < 0)
+                   || (config_list[i].iChannel >= instance->channels)) {
+                       PERROR("Invalid channel number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+                       goto ERROR;
+               }
+       }
+
+       // Check settings - end
+
+       //Cancel control task
+       PDEBUG("Cancel control task.\n");
+       instance->ai_control_task_flag = 0;
+       cancel_delayed_work(&instance->ai_control_task);
+
+       // Work around from Keith Hartley - begin
+       if (trigger->iScanStartTrigType == ME_TRIG_TYPE_TIMER) {
+               if (count == 1) {
+                       // The hardware does not work properly with a non-zero scan time
+                       // if there is only ONE channel in the channel list. In this case
+                       // we must set the scan time to zero and use the channel time.
+
+                       conv_ticks = scan_ticks;
+                       trigger->iScanStartTrigType = ME_TRIG_TYPE_FOLLOW;
+               } else if (scan_ticks == count * conv_ticks) {
+                       // Another hardware problem. If the number of scan ticks is
+                       // exactly equal to the number of channel ticks multiplied by
+                       // the number of channels then the sampling rate is reduced
+                       // by half.
+                       trigger->iScanStartTrigType = ME_TRIG_TYPE_FOLLOW;
+               }
+       }
+       // Work around from Keith Hartley - end
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       if (inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM) {
+               PERROR("Subdevice is busy.\n");
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+               ME_SUBDEVICE_EXIT;
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       instance->status = ai_status_none;
+       spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+       // Stop all actions. Block all interrupts. Clear (disable) FIFOs.
+       ctrl =
+           ME4600_AI_CTRL_BIT_LE_IRQ_RESET | ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
+           ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+
+       tmp = inl(instance->ctrl_reg);
+       // Preserve EXT IRQ and OFFSET settings. Clean other bits.
+       tmp &=
+           (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET |
+            ME4600_AI_CTRL_BIT_FULLSCALE | ME4600_AI_CTRL_BIT_OFFSET);
+
+       // Send it to register.
+       outl(tmp | ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp | ctrl);
+
+       // Enable channel fifo -> data fifo in stream_start().
+       ctrl |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO;
+       outl(tmp | ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp | ctrl);
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+       // Write the channel list
+       for (i = 0; i < count; i++) {
+               entry = config_list[i].iChannel;
+
+               switch (config_list[i].iStreamConfig) {
+               case 0: //BIPOLAR 10V
+/*
+                               // ME4600_AI_LIST_RANGE_BIPOLAR_10 = 0x0000
+                               // 'entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10' <== Do nothing. Removed.
+                               entry |= ME4600_AI_LIST_RANGE_BIPOLAR_10;
+*/
+                       break;
+               case 1: //UNIPOLAR 10V
+                       entry |= ME4600_AI_LIST_RANGE_UNIPOLAR_10;
+                       break;
+               case 2: //BIPOLAR 2.5V
+                       entry |= ME4600_AI_LIST_RANGE_BIPOLAR_2_5;
+                       break;
+               case 3: //UNIPOLAR 2.5V
+                       entry |= ME4600_AI_LIST_RANGE_UNIPOLAR_2_5;
+                       break;
+               default:
+                       PERROR_CRITICAL("UNCHECK ERROR in config_list!\n");
+                       PERROR_CRITICAL
+                           ("WRONG range\nPosition:%d Range:0x%04X\n", i,
+                            config_list[i].iStreamConfig);
+                       goto VERIFY_ERROR;
+                       break;
+               }
+
+               switch (config_list[i].iRef) {
+               case ME_REF_AI_GROUND:  //SINGLE ENDED
+/*
+                               // ME4600_AI_LIST_INPUT_SINGLE_ENDED = 0x0000
+                               // 'entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED' ==> Do nothing. Removed.
+                               entry |= ME4600_AI_LIST_INPUT_SINGLE_ENDED;
+*/ break;
+               case ME_REF_AI_DIFFERENTIAL:    //DIFFERENTIAL
+                       entry |= ME4600_AI_LIST_INPUT_DIFFERENTIAL;
+                       break;
+               default:
+                       PERROR_CRITICAL("UNCHECK ERROR in config_list!\n");
+                       PERROR_CRITICAL
+                           ("WRONG reference\nPosition:%d Reference:0x%04X\n",
+                            i, config_list[i].iRef);
+                       goto VERIFY_ERROR;
+                       break;
+               }
+
+               //Add last entry flag
+               if (i == (count - 1)) {
+                       entry |= ME4600_AI_LIST_LAST_ENTRY;
+               }
+
+               outl(entry, instance->channel_list_reg);
+               PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->channel_list_reg - instance->reg_base,
+                          entry);
+       }
+
+       // Set triggering registers
+       --acq_ticks;
+       outl(acq_ticks, instance->chan_pre_timer_reg);
+       PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%llX\n",
+                  instance->reg_base,
+                  instance->chan_pre_timer_reg - instance->reg_base,
+                  acq_ticks);
+       outl(acq_ticks, instance->scan_pre_timer_low_reg);
+       PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%llX\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_low_reg - instance->reg_base,
+                  acq_ticks & 0xFFFFFFFF);
+       outl((acq_ticks >> 32), instance->scan_pre_timer_high_reg);
+       PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%llX\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_high_reg - instance->reg_base,
+                  (acq_ticks >> 32) & 0xFFFFFFFF);
+
+       // Set triggers
+       switch (trigger->iAcqStartTrigType) {
+               // Internal
+       case ME_TRIG_TYPE_SW:
+               // Nothing to set.
+               break;
+
+               // External
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               ctrl |= ME4600_AI_CTRL_BIT_EX_TRIG_ANALOG;
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               ctrl |= ME4600_AI_CTRL_BIT_EX_TRIG;
+
+               // External trigger needs edge's definition
+               switch (trigger->iAcqStartTrigEdge) {
+               case ME_TRIG_EDGE_RISING:
+                       // Nothing to set.
+                       break;
+
+               case ME_TRIG_EDGE_FALLING:
+                       ctrl |= ME4600_AI_CTRL_BIT_EX_TRIG_FALLING;
+                       break;
+
+               case ME_TRIG_EDGE_ANY:
+                       ctrl |=
+                           ME4600_AI_CTRL_BIT_EX_TRIG_FALLING |
+                           ME4600_AI_CTRL_BIT_EX_TRIG_BOTH;
+                       break;
+
+               default:
+                       PERROR_CRITICAL
+                           ("UNCHECK TRIGGER EDGE in triggers structure!\n");
+                       PERROR_CRITICAL
+                           ("WRONG acquisition start trigger:0x%04X.\n",
+                            trigger->iAcqStartTrigEdge);
+                       err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+                       goto VERIFY_ERROR;
+                       break;
+               }
+               break;
+
+       default:
+               PERROR_CRITICAL("UNCHECK TRIGGER in triggers structure!\n");
+               PERROR_CRITICAL("WRONG acquisition start trigger:0x%04X.\n",
+                               trigger->iAcqStartTrigType);
+               err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;
+               goto VERIFY_ERROR;
+               break;
+       }
+
+       switch (trigger->iScanStartTrigType) {
+       case ME_TRIG_TYPE_TIMER:
+               --scan_ticks;
+               outl(scan_ticks, instance->scan_timer_low_reg);
+               PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%llX\n",
+                          instance->reg_base,
+                          instance->scan_timer_low_reg - instance->reg_base,
+                          scan_ticks & 0xFFFFFFFF);
+               outl((scan_ticks >> 32), instance->scan_timer_high_reg);
+               PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%llX\n",
+                          instance->reg_base,
+                          instance->scan_timer_high_reg - instance->reg_base,
+                          (scan_ticks >> 32) & 0xFFFFFFFF);
+
+               if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) {
+                       ctrl |= ME4600_AI_CTRL_BIT_MODE_0;
+               } else {
+                       ctrl |= ME4600_AI_CTRL_BIT_MODE_1;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               outl(0, instance->scan_timer_low_reg);
+               PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->scan_timer_low_reg - instance->reg_base,
+                          0);
+               outl(0, instance->scan_timer_high_reg);
+               PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->scan_timer_high_reg - instance->reg_base,
+                          0);
+               ctrl |= ME4600_AI_CTRL_BIT_MODE_2;
+               break;
+
+       case ME_TRIG_TYPE_FOLLOW:
+               outl(0, instance->scan_timer_low_reg);
+               PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->scan_timer_low_reg - instance->reg_base,
+                          0);
+               outl(0, instance->scan_timer_high_reg);
+               PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->scan_timer_high_reg - instance->reg_base,
+                          0);
+
+               if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) {
+                       ctrl |= ME4600_AI_CTRL_BIT_MODE_0;
+               } else {
+                       ctrl |= ME4600_AI_CTRL_BIT_MODE_1;
+               }
+               break;
+
+       default:
+               PERROR_CRITICAL("UNCHECK TRIGGER in triggers structure!\n");
+               PERROR_CRITICAL("WRONG scan start trigger:0x%04X.\n",
+                               trigger->iScanStartTrigType);
+               err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+               goto VERIFY_ERROR;
+               break;
+       }
+
+       switch (trigger->iConvStartTrigType) {
+
+       case ME_TRIG_TYPE_TIMER:
+               --conv_ticks;
+               outl(conv_ticks, instance->chan_timer_reg);
+               PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%llX\n",
+                          instance->reg_base,
+                          instance->chan_timer_reg - instance->reg_base,
+                          conv_ticks);
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+       case ME_TRIG_TYPE_EXT_ANALOG:
+               outl(0, instance->chan_timer_reg);
+               PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->chan_timer_reg - instance->reg_base, 0);
+               ctrl |= ME4600_AI_CTRL_BIT_MODE_0 | ME4600_AI_CTRL_BIT_MODE_1;
+               break;
+
+       default:
+               PERROR_CRITICAL("UNCHECK TRIGGER in triggers structure!\n");
+               PERROR_CRITICAL("WRONG conv start trigger:0x%04X.\n",
+                               trigger->iConvStartTrigType);
+               err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+               goto VERIFY_ERROR;
+
+               break;
+       }
+
+       //Sample & Hold feature
+       if (flags & ME_IO_STREAM_CONFIG_SAMPLE_AND_HOLD) {
+               if (instance->sh) {
+                       ctrl |= ME4600_AI_CTRL_BIT_SAMPLE_HOLD;
+               } else {
+                       PERROR_CRITICAL("UNCHECK S&H feature!\n");
+                       err = ME_ERRNO_INVALID_FLAGS;
+                       goto VERIFY_ERROR;
+               }
+       }
+       //Enable IRQs sources but leave latches blocked.
+       ctrl |= (ME4600_AI_CTRL_BIT_HF_IRQ | ME4600_AI_CTRL_BIT_SC_IRQ | ME4600_AI_CTRL_BIT_LE_IRQ);    //The last IRQ source (ME4600_AI_CTRL_BIT_LE_IRQ) is unused!
+
+       //Everything is good. Finalize
+       spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+       tmp = inl(instance->ctrl_reg);
+
+       //Preserve EXT IRQ and OFFSET settings. Clean other bits.
+       tmp &=
+           (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET |
+            ME4600_AI_CTRL_BIT_FULLSCALE | ME4600_AI_CTRL_BIT_OFFSET);
+
+       // write the control word
+       outl(ctrl | tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl | tmp);
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+       //Set the global parameters end exit.
+       instance->chan_list_len = count;
+       instance->fifo_irq_threshold = fifo_irq_threshold;
+
+       if (trigger->iAcqStopTrigType == ME_TRIG_TYPE_COUNT) {
+               data_required =
+                   (unsigned long long)trigger->iAcqStopCount *
+                   (unsigned long long)count;
+               if (data_required > UINT_MAX)
+                       data_required = UINT_MAX;
+               instance->data_required = (unsigned int)data_required;
+       } else if (trigger->iScanStopTrigType == ME_TRIG_TYPE_COUNT)
+               instance->data_required =
+                   (unsigned long long)trigger->iScanStopCount;
+       else
+               instance->data_required = 0;
+
+       // Mark subdevice as configured to work in stream mode.
+       instance->status = ai_status_stream_configured;
+
+       // Deinit single config. Set all entries to NOT_CONFIGURED.
+       for (i = 0; i < instance->channels; i++) {
+               instance->single_config[i].status =
+                   ME_SINGLE_CHANNEL_NOT_CONFIGURED;
+       }
+
+      VERIFY_ERROR:            // Error in code. Wrong setting check. This should never ever happend!
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+      ERROR:                   // Error in settings.
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ai_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long t;
+       unsigned long j;
+       int volatile head;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       } else {                // Max time.
+               t = LONG_MAX;
+       }
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       j = jiffies;
+
+       while (1) {
+               // Only runing device can generate break.
+               head = instance->circ_buf.head;
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((head !=
+                                                  instance->circ_buf.head)
+                                                 ||
+                                                 ((instance->status <=
+                                                   ai_status_stream_run_wait)
+                                                  && (instance->status >=
+                                                      ai_status_stream_end_wait))),
+                                                t);
+
+               if (head != instance->circ_buf.head) {  // New data in buffer.
+                       break;
+               } else if (instance->status == ai_status_stream_end) {  // End of work.
+                       break;
+               } else if (instance->status == ai_status_stream_fifo_error) {
+                       err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+                       break;
+               } else if (instance->status == ai_status_stream_buffer_error) {
+                       err = ME_ERRNO_RING_BUFFER_OVERFLOW;
+                       break;
+               } else if (instance->status == ai_status_stream_error) {
+                       err = ME_ERRNO_INTERNAL;
+                       break;
+               } else if ((jiffies - j) >= t) {
+                       PERROR("Wait on values timed out.\n");
+                       err = ME_ERRNO_TIMEOUT;
+                       break;
+               } else if (signal_pending(current)) {
+                       PERROR("Wait on values interrupted from signal.\n");
+                       err = ME_ERRNO_SIGNAL;
+                       break;
+               }
+               // Correct timeout.
+               t -= jiffies - j;
+       }
+
+       *count = me_circ_buf_values(&instance->circ_buf);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int inline me4600_ai_io_stream_read_get_value(me4600_ai_subdevice_t *
+                                                    instance, int *values,
+                                                    const int count,
+                                                    const int flags)
+{
+       int n;
+       int i;
+       uint32_t value;
+
+       ///Checking how many datas can be copied.
+       n = me_circ_buf_values(&instance->circ_buf);
+       if (n <= 0)
+               return 0;
+
+       if (n > count)
+               n = count;
+
+       if (flags & ME_IO_STREAM_READ_FRAMES) {
+               if (n < instance->chan_list_len)        //Not enough data!
+                       return 0;
+               n -= n % instance->chan_list_len;
+       }
+
+       for (i = 0; i < n; i++) {
+               value = *(instance->circ_buf.buf + instance->circ_buf.tail);
+               if (put_user(value, values + i)) {
+                       PERROR("Cannot copy new values to user.\n");
+                       return -ME_ERRNO_INTERNAL;
+               }
+               instance->circ_buf.tail++;
+               instance->circ_buf.tail &= instance->circ_buf.mask;
+       }
+       return n;
+}
+
+static int me4600_ai_io_stream_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int read_mode,
+                                   int *values, int *count, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       int ret;
+
+       int c = *count;
+       int min = c;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (flags & ~ME_IO_STREAM_READ_FRAMES) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!values || !count) {
+               PERROR("Request has invalid pointer.\n");
+               return ME_ERRNO_INVALID_POINTER;
+       }
+
+       if (c < 0) {
+               PERROR("Request has invalid value's counter.\n");
+               return ME_ERRNO_INVALID_VALUE_COUNT;
+       }
+
+       if ((read_mode != ME_READ_MODE_BLOCKING)
+           && (read_mode != ME_READ_MODE_NONBLOCKING)) {
+               PERROR("Invalid read mode specified.\n");
+               return ME_ERRNO_INVALID_READ_MODE;
+       }
+
+       if (c == 0) {           //You get what you want! Nothing more or less.
+               return ME_ERRNO_SUCCESS;
+       }
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+       ME_SUBDEVICE_ENTER;
+
+       //Check if subdevice is configured.
+       if (instance->chan_list_len <= 0) {
+               PERROR("Subdevice wasn't configured.\n");
+               ME_SUBDEVICE_EXIT;
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       if (flags & ME_IO_STREAM_READ_FRAMES) {
+               if (c < instance->chan_list_len) {      //Not enough data requested.
+                       PERROR
+                           ("When using FRAME_READ mode minimal size is defined by channel list.\n");
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INVALID_VALUE_COUNT;
+               }
+       }
+
+       if (c > (ME4600_AI_CIRC_BUF_COUNT - instance->chan_list_len)) { // To return acceptable amount of data when user pass too big value.
+               min = ME4600_AI_CIRC_BUF_COUNT - instance->chan_list_len;
+       }
+
+       if (flags & ME_IO_STREAM_READ_FRAMES) {
+               //Wait for whole list.
+               if (read_mode == ME_READ_MODE_BLOCKING) {
+                       min = c - (c % instance->chan_list_len);
+               }
+
+               if (read_mode == ME_READ_MODE_NONBLOCKING) {
+                       min = instance->chan_list_len;
+               }
+       }
+
+       if ((inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM)) {   //Working
+               //If blocking mode -> wait for data.
+               if ((me_circ_buf_values(&instance->circ_buf) < min)
+                   && (read_mode == ME_READ_MODE_BLOCKING)) {
+                       wait_event_interruptible(instance->wait_queue,
+                                                ((me_circ_buf_values
+                                                  (&instance->circ_buf) >= min)
+                                                 || !(inl(instance->status_reg)
+                                                      &
+                                                      ME4600_AI_STATUS_BIT_FSM)));
+
+                       if (signal_pending(current)) {
+                               PERROR
+                                   ("Wait on values interrupted from signal.\n");
+                               err = ME_ERRNO_SIGNAL;
+                       }
+               }
+       }
+
+       ret = me4600_ai_io_stream_read_get_value(instance, values, c, flags);
+       if (ret < 0) {
+               err = -ret;
+               *count = 0;
+       } else if (ret == 0) {
+               *count = 0;
+               if (instance->status == ai_status_stream_fifo_error) {
+                       err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+                       instance->status = ai_status_stream_end;
+               } else if (instance->status == ai_status_stream_buffer_error) {
+                       err = ME_ERRNO_RING_BUFFER_OVERFLOW;
+                       instance->status = ai_status_stream_end;
+               } else if (instance->status == ai_status_stream_end) {
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+               } else if (instance->status == ai_status_stream_error) {
+                       err = ME_ERRNO_INTERNAL;
+               } else if (instance->status == ai_status_none) {
+                       PDEBUG("Stream canceled.\n");
+                       err = ME_ERRNO_INTERNAL;
+               }
+       } else {
+               *count = ret;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+/** @brief Stop aqusation. Preserve FIFOs.
+*
+* @param instance The subdevice instance (pointer).
+*/
+
+static int ai_stop_immediately(me4600_ai_subdevice_t * instance)
+{
+       unsigned long cpu_flags = 0;
+       volatile uint32_t ctrl;
+       const int timeout = HZ / 10;    //100ms
+       int i;
+
+       for (i = 0; i <= timeout; i++) {
+               spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME4600_AI_CTRL_BIT_STOP;
+               ctrl |=
+                   (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP |
+                    ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
+                    ME4600_AI_CTRL_BIT_SC_IRQ_RESET);
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+               if (!(inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM)) {  // Exit.
+                       break;
+               }
+
+               PINFO("Wait for stop: %d\n", i + 1);
+               //Still working!
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+       }
+
+       if (i > timeout) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               return ME_ERRNO_INTERNAL;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags = 0;
+       unsigned long ref;
+       unsigned long delay = 0;
+
+       volatile uint32_t tmp;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((start_mode != ME_START_MODE_BLOCKING)
+           && (start_mode != ME_START_MODE_NONBLOCKING)) {
+               PERROR("Invalid start mode specified.\n");
+               return ME_ERRNO_INVALID_START_MODE;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       ME_SUBDEVICE_ENTER
+           spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+
+       tmp = inl(instance->ctrl_reg);
+
+       if ((tmp & ME4600_AI_STATUS_BIT_FSM)) {
+               PERROR("Conversion is already running.\n");
+               spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+               err = ME_ERRNO_SUBDEVICE_BUSY;
+               goto ERROR;
+       }
+
+       if (instance->chan_list_len == 0) {     //Not configured!
+               PERROR("Subdevice is not configured to work in stream mode!\n");
+               spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+               goto ERROR;
+       }
+
+       if (!(tmp & (ME4600_AI_CTRL_BIT_MODE_0 | ME4600_AI_CTRL_BIT_MODE_1 | ME4600_AI_CTRL_BIT_MODE_2))) {     //Mode 0 = single work => no stream config
+               PERROR("Subdevice is configured to work in single mode.\n");
+               spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+               goto ERROR;
+       }
+       //Reset stop bits.
+       tmp |= ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | ME4600_AI_CTRL_BIT_STOP;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       //Start datas' FIFO.
+       tmp |= ME4600_AI_CTRL_BIT_DATA_FIFO;
+       //Free stop bits.
+       tmp &= ~(ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | ME4600_AI_CTRL_BIT_STOP);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+       //Cancel control task
+       PDEBUG("Cancel control task.\n");
+       instance->ai_control_task_flag = 0;
+       cancel_delayed_work(&instance->ai_control_task);
+
+       //Set the starting values.
+       instance->ISM.global_read = 0;
+       instance->ISM.read = 0;
+       //Clear circular buffer
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+
+       //Set everything.
+       ai_data_acquisition_logic(instance);
+
+       //Set status to 'wait for start'
+       instance->status = ai_status_stream_run_wait;
+
+       // Set control task's timeout
+       instance->timeout.delay = delay;
+       instance->timeout.start_time = jiffies;
+
+       //Lets go! Start work
+       inl(instance->start_reg);
+       PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base,
+                  instance->start_reg - instance->reg_base);
+
+       // Schedule control task
+       instance->ai_control_task_flag = 1;
+       queue_delayed_work(instance->me4600_workqueue,
+                          &instance->ai_control_task, 1);
+
+       PDEVELOP("Delay:%ld\n", delay);
+
+       if (start_mode == ME_START_MODE_BLOCKING) {     //Wait for start.
+               ref = jiffies;
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ai_status_stream_run_wait),
+                                                (delay) ? delay +
+                                                1 : LONG_MAX);
+
+               if ((instance->status != ai_status_stream_run)
+                   && (instance->status != ai_status_stream_end)) {
+                       PDEBUG("Starting stream canceled. %d\n",
+                              instance->status);
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->status = ai_status_none;
+                       ai_stop_isr(instance);
+                       err = ME_ERRNO_SIGNAL;
+               } else if ((delay) && ((jiffies - ref) > delay)) {
+                       if (instance->status != ai_status_stream_run) {
+                               if (instance->status == ai_status_stream_end) {
+                                       PDEBUG("Timeout reached.\n");
+                               } else if ((jiffies - ref) > delay + 1) {
+                                       PERROR
+                                           ("Timeout reached. Not handled by control task!\n");
+                                       ai_stop_isr(instance);
+                                       instance->status =
+                                           ai_status_stream_error;
+                               } else {
+                                       PERROR
+                                           ("Timeout reached. Signal come but status is strange: %d\n",
+                                            instance->status);
+                                       ai_stop_isr(instance);
+                                       instance->status =
+                                           ai_status_stream_error;
+                               }
+
+                               instance->ai_control_task_flag = 0;
+                               cancel_delayed_work(&instance->ai_control_task);
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               }
+       }
+#ifdef MEDEBUG_INFO
+       tmp = inl(instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       PINFO("STATUS_BIT_FSM=%s.\n",
+             (tmp & ME4600_AI_STATUS_BIT_FSM) ? "on" : "off");
+       PINFO("CTRL_BIT_HF_IRQ=%s.\n",
+             (tmp & ME4600_AI_CTRL_BIT_HF_IRQ) ? "enable" : "disable");
+       PINFO("CTRL_BIT_HF_IRQ_RESET=%s.\n",
+             (tmp & ME4600_AI_CTRL_BIT_HF_IRQ_RESET) ? "reset" : "work");
+       PINFO("CTRL_BIT_SC_IRQ=%s.\n",
+             (tmp & ME4600_AI_CTRL_BIT_SC_IRQ) ? "enable" : "disable");
+       PINFO("CTRL_BIT_SC_RELOAD=%s.\n",
+             (tmp & ME4600_AI_CTRL_BIT_SC_RELOAD) ? "on" : "off");
+       PINFO("CTRL_BIT_SC_IRQ_RESET=%s.\n",
+             (tmp & ME4600_AI_CTRL_BIT_SC_IRQ_RESET) ? "reset" : "work");
+#endif
+
+      ERROR:
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ai_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       switch (instance->status) {
+       case ai_status_single_configured:
+       case ai_status_stream_configured:
+       case ai_status_stream_end:
+       case ai_status_stream_fifo_error:
+       case ai_status_stream_buffer_error:
+       case ai_status_stream_error:
+               *status = ME_STATUS_IDLE;
+               break;
+
+       case ai_status_stream_run_wait:
+       case ai_status_stream_run:
+       case ai_status_stream_end_wait:
+               *status = ME_STATUS_BUSY;
+               break;
+
+       case ai_status_none:
+       default:
+               *status =
+                   (inl(instance->status_reg) & ME4600_AI_STATUS_BIT_FSM) ?
+                   ME_STATUS_BUSY : ME_STATUS_IDLE;
+               break;
+       }
+
+       if ((wait == ME_WAIT_IDLE) && (*status == ME_STATUS_BUSY)) {
+               // Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((instance->status !=
+                                                  ai_status_stream_run_wait)
+                                                 && (instance->status !=
+                                                     ai_status_stream_run)
+                                                 && (instance->status !=
+                                                     ai_status_stream_end_wait)),
+                                                LONG_MAX);
+
+               if (instance->status != ai_status_stream_end) {
+                       PDEBUG("Wait for IDLE canceled. %d\n",
+                              instance->status);
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait for IDLE interrupted.\n");
+                       instance->status = ai_status_none;
+                       ai_stop_isr(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               *status = ME_STATUS_IDLE;
+       }
+
+       *values = me_circ_buf_values(&instance->circ_buf);
+       PDEBUG("me_circ_buf_values(&instance->circ_buf)=%d.\n", *values);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ai_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags)
+{
+/**
+ @note Stop is implemented only in blocking mode.
+ @note Function return when state machine is stoped.
+*/
+       me4600_ai_subdevice_t *instance;
+       unsigned long cpu_flags;
+       uint32_t ctrl;
+       int ret;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((stop_mode != ME_STOP_MODE_IMMEDIATE)
+           && (stop_mode != ME_STOP_MODE_LAST_VALUE)) {
+               PERROR("Invalid stop mode specified.\n");
+               return ME_ERRNO_INVALID_STOP_MODE;
+       }
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       // Mark as stopping. => Software stop.
+       instance->status = ai_status_stream_end_wait;
+
+       if (stop_mode == ME_STOP_MODE_IMMEDIATE) {
+               ret = ai_stop_immediately(instance);
+
+               if (ret) {
+                       PERROR("FSM is still busy.\n");
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_SUBDEVICE_BUSY;
+               }
+               instance->ai_control_task_flag = 0;
+
+       } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) {
+               // Set stop bit in registry.
+               spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+               ctrl = inl(instance->ctrl_reg);
+               ctrl |= ME4600_AI_CTRL_BIT_STOP;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+
+               // Only runing process will interrupt this call. Events are signaled when status change.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ai_status_stream_end_wait),
+                                                LONG_MAX);
+
+               if (instance->status != ai_status_stream_end) {
+                       PDEBUG("Stopping stream canceled.\n");
+                       ret = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Stopping stream interrupted.\n");
+                       instance->status = ai_status_none;
+                       ret = ME_ERRNO_SIGNAL;
+               }
+               // End of work.
+               ai_stop_immediately(instance);
+
+       }
+
+       ret = ai_read_data_pooling(instance);
+       if (ret > 0) {          // Everything fine. More datas put to software buffer.
+               instance->status = ai_status_stream_end;
+               ret = ME_ERRNO_SUCCESS;
+               // Signal that we put last data to software buffer.
+               wake_up_interruptible_all(&instance->wait_queue);
+       } else if (ret == 0) {  // Everything fine. No more datas in FIFO.
+               instance->status = ai_status_stream_end;
+               ret = ME_ERRNO_SUCCESS;
+       } else if (ret == -ME_ERRNO_RING_BUFFER_OVERFLOW) {     // Stop is unsuccessful, buffer is overflow.
+               instance->status = ai_status_stream_buffer_error;
+               ret = ME_ERRNO_SUCCESS;
+       } else {                // Stop is unsuccessful
+               instance->status = ai_status_stream_end;
+               ret = -ret;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return ret;
+}
+
+static int me4600_ai_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range)
+{
+       me4600_ai_subdevice_t *instance;
+       int i;
+       int r = -1;
+       int diff = 21E6;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if ((*max - *min) < 0) {
+               PERROR("Invalid minimum and maximum values specified.\n");
+               return ME_ERRNO_INVALID_MIN_MAX;
+       }
+
+       if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
+               for (i = 0; i < instance->ranges_len; i++) {
+                       if ((instance->ranges[i].min <= *min)
+                           && ((instance->ranges[i].max + 1000) >= *max)) {
+                               if ((instance->ranges[i].max -
+                                    instance->ranges[i].min) - (*max - *min) <
+                                   diff) {
+                                       r = i;
+                                       diff =
+                                           (instance->ranges[i].max -
+                                            instance->ranges[i].min) - (*max -
+                                                                        *min);
+                               }
+                       }
+               }
+
+               if (r < 0) {
+                       PERROR("No matching range found.\n");
+                       return ME_ERRNO_NO_RANGE;
+               } else {
+                       *min = instance->ranges[r].min;
+                       *max = instance->ranges[r].max;
+                       *maxdata = ME4600_AI_MAX_DATA;
+                       *range = r;
+               }
+       } else {
+               PERROR("Invalid physical unit specified.\n");
+               return ME_ERRNO_INVALID_UNIT;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count)
+{
+       me4600_ai_subdevice_t *instance;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
+               *count = instance->ranges_len;
+       } else {
+               *count = 0;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata)
+{
+       me4600_ai_subdevice_t *instance;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       if ((range < instance->ranges_len) && (range >= 0)) {
+               *unit = ME_UNIT_VOLT;
+               *min = instance->ranges[range].min;
+               *max = instance->ranges[range].max;
+               *maxdata = ME4600_AI_MAX_DATA;
+       } else {
+               PERROR("Invalid range number specified.\n");
+               return ME_ERRNO_INVALID_RANGE;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_timer(me_subdevice_t * subdevice,
+                                int timer,
+                                int *base_frequency,
+                                long long *min_ticks, long long *max_ticks)
+{
+       me4600_ai_subdevice_t *instance;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       switch (timer) {
+
+       case ME_TIMER_ACQ_START:
+               *base_frequency = ME4600_AI_BASE_FREQUENCY;
+               *min_ticks = ME4600_AI_MIN_ACQ_TICKS;
+               *max_ticks = ME4600_AI_MAX_ACQ_TICKS;
+               break;
+
+       case ME_TIMER_SCAN_START:
+               *base_frequency = ME4600_AI_BASE_FREQUENCY;
+               *min_ticks = ME4600_AI_MIN_SCAN_TICKS;
+               *max_ticks = ME4600_AI_MAX_SCAN_TICKS;
+               break;
+
+       case ME_TIMER_CONV_START:
+               *base_frequency = ME4600_AI_BASE_FREQUENCY;
+               *min_ticks = ME4600_AI_MIN_CHAN_TICKS;
+               *max_ticks = ME4600_AI_MAX_CHAN_TICKS;
+               break;
+
+       default:
+               PERROR("Invalid timer specified.(0x%04x)\n", timer);
+
+               return ME_ERRNO_INVALID_TIMER;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       me4600_ai_subdevice_t *instance;
+
+       PDEBUG("executed. idx=0\n");
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+       *number = instance->channels;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed. idx=0\n");
+
+       *type = ME_TYPE_AI;
+       *subtype = ME_SUBTYPE_STREAMING;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed. idx=0\n");
+
+       *caps =
+           ME_CAPS_AI_TRIG_SYNCHRONOUS | ME_CAPS_AI_FIFO |
+           ME_CAPS_AI_FIFO_THRESHOLD;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ai_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                              int cap, int *args, int count)
+{
+       me4600_ai_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       instance = (me4600_ai_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (count != 1) {
+               PERROR("Invalid capability argument count.\n");
+               return ME_ERRNO_INVALID_CAP_ARG_COUNT;
+       }
+
+       switch (cap) {
+       case ME_CAP_AI_FIFO_SIZE:
+               args[0] = ME4600_AI_FIFO_COUNT;
+               break;
+
+       case ME_CAP_AI_BUFFER_SIZE:
+               args[0] =
+                   (instance->circ_buf.buf) ? ME4600_AI_CIRC_BUF_COUNT : 0;
+               break;
+
+       default:
+               PERROR("Invalid capability.\n");
+               err = ME_ERRNO_INVALID_CAP;
+               args[0] = 0;
+       }
+
+       return err;
+}
+
+void ai_limited_isr(me4600_ai_subdevice_t * instance, const uint32_t irq_status,
+                   const uint32_t ctrl_status)
+{
+       int to_read;
+
+       if (!instance->fifo_irq_threshold) {    //No threshold provided. SC ends work. HF need reseting.
+               if (irq_status & ME4600_IRQ_STATUS_BIT_SC) {
+                       if (ai_read_data(instance, instance->ISM.next) != instance->ISM.next) { //ERROR!
+                               PERROR
+                                   ("Limited amounts aqusition with TH=0: Circular buffer full!\n");
+                               instance->status =
+                                   ai_status_stream_buffer_error;
+                       } else {
+                               instance->status = ai_status_stream_end;
+                       }
+                       //End of work.
+                       ai_stop_isr(instance);
+               } else if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) {
+                       instance->ISM.global_read += ME4600_AI_FIFO_HALF;
+
+                       if (ai_read_data(instance, ME4600_AI_FIFO_HALF) != ME4600_AI_FIFO_HALF) {       //ERROR!
+                               PERROR
+                                   ("Limited amounts aqusition with TH = 0: Circular buffer full!\n");
+                               //End of work.
+                               ai_stop_isr(instance);
+                               instance->status =
+                                   ai_status_stream_buffer_error;
+                       } else {
+                               //Continue.
+                               ai_limited_ISM(instance, irq_status);
+                       }
+               }
+               //Signal user.
+               wake_up_interruptible_all(&instance->wait_queue);
+       } else                  //if(instance->fifo_irq_threshold)
+       {
+               if (irq_status & ME4600_IRQ_STATUS_BIT_SC) {
+                       instance->ISM.read = 0;
+                       if ((instance->fifo_irq_threshold < ME4600_AI_FIFO_HALF)
+                           && (!(ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA)))
+                       {
+                               to_read =
+                                   ME4600_AI_FIFO_HALF -
+                                   (ME4600_AI_FIFO_HALF %
+                                    instance->fifo_irq_threshold);
+                               PDEBUG
+                                   ("Limited amounts aqusition with TH != 0: Not fast enough data aqusition! correction=%d\n",
+                                    to_read);
+                       } else {
+                               to_read = instance->ISM.next;
+                       }
+                       instance->ISM.global_read += to_read;
+
+                       ai_reschedule_SC(instance);
+
+                       if (ai_read_data(instance, to_read) != to_read) {       //ERROR!
+                               PERROR
+                                   ("Limited amounts aqusition with TH != 0: Circular buffer full!\n");
+                               //End of work.
+                               ai_stop_isr(instance);
+                               instance->status =
+                                   ai_status_stream_buffer_error;
+                       } else {
+                               //Continue.
+                               ai_limited_ISM(instance, irq_status);
+                       }
+
+                       //Signal user.
+                       wake_up_interruptible_all(&instance->wait_queue);
+               } else if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) {
+                       instance->ISM.read += ME4600_AI_FIFO_HALF;
+                       instance->ISM.global_read += ME4600_AI_FIFO_HALF;
+
+                       if (ai_read_data(instance, ME4600_AI_FIFO_HALF) != ME4600_AI_FIFO_HALF) {       //ERROR!
+                               PERROR
+                                   ("Limited amounts aqusition with TH != 0: Circular buffer full!\n");
+                               ai_stop_isr(instance);
+
+                               instance->status =
+                                   ai_status_stream_buffer_error;
+                               //Signal user.
+                               wake_up_interruptible_all(&instance->
+                                                         wait_queue);
+                       } else {
+                               //Countinue.
+                               ai_limited_ISM(instance, irq_status);
+                       }
+               }
+
+               if (instance->ISM.global_read >= instance->data_required) {     //End of work. Next paranoid pice of code: '>=' instead od '==' only to be sure.
+                       ai_stop_isr(instance);
+                       if (instance->status < ai_status_stream_end) {
+                               instance->status = ai_status_stream_end;
+                       }
+#ifdef MEDEBUG_ERROR
+                       if (instance->ISM.global_read > instance->data_required) {      //This is security check case. This should never ever happend!
+                               PERROR
+                                   ("Limited amounts aqusition: Read more data than necessary! data_required=%d < read=%d\n",
+                                    instance->data_required,
+                                    instance->ISM.global_read);
+                               //Signal error (warning??).
+                               instance->status = ai_status_stream_error;
+                       }
+#endif
+               }
+       }
+}
+
+void ai_infinite_isr(me4600_ai_subdevice_t * instance,
+                    const uint32_t irq_status, const uint32_t ctrl_status)
+{
+       int to_read;
+
+       if (irq_status & ME4600_IRQ_STATUS_BIT_SC) {    //next chunck of data -> read fifo
+               //Set new state in ISM.
+               if ((instance->fifo_irq_threshold < ME4600_AI_FIFO_HALF) && (!(ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA))) {  //There is more data than we ecpected. Propably we aren't fast enough. Read as many as possible.
+                       if (instance->fifo_irq_threshold) {
+                               to_read =
+                                   ME4600_AI_FIFO_HALF -
+                                   (ME4600_AI_FIFO_HALF %
+                                    instance->fifo_irq_threshold);
+                               if (to_read > instance->fifo_irq_threshold) {
+                                       PDEBUG
+                                           ("Infinite aqusition: Not fast enough data aqusition! TH != 0: correction=%d\n",
+                                            to_read);
+                               }
+                       } else {        //No threshold specified.
+                               to_read = ME4600_AI_FIFO_HALF;
+                       }
+               } else {
+                       to_read = instance->ISM.next;
+               }
+
+               instance->ISM.read += to_read;
+
+               //Get data
+               if (ai_read_data(instance, to_read) != to_read) {       //ERROR!
+                       PERROR("Infinite aqusition: Circular buffer full!\n");
+                       ai_stop_isr(instance);
+                       instance->status = ai_status_stream_buffer_error;
+               } else {
+                       ai_infinite_ISM(instance);
+                       instance->ISM.global_read += instance->ISM.read;
+                       instance->ISM.read = 0;
+               }
+
+               //Signal data to user
+               wake_up_interruptible_all(&instance->wait_queue);
+       } else if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) {  //fifo is half full -> read fifo       Large blocks only!
+               instance->ISM.read += ME4600_AI_FIFO_HALF;
+
+               if (ai_read_data(instance, ME4600_AI_FIFO_HALF) != ME4600_AI_FIFO_HALF) {       //ERROR!
+                       PERROR("Infinite aqusition: Circular buffer full!\n");
+                       ai_stop_isr(instance);
+                       instance->status = ai_status_stream_buffer_error;
+
+                       //Signal it.
+                       wake_up_interruptible_all(&instance->wait_queue);
+               } else {
+                       ai_infinite_ISM(instance);
+               }
+       }
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me4600_ai_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me4600_ai_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{                              /// @note This is time critical function!
+       uint32_t irq_status;
+       uint32_t ctrl_status;
+       me4600_ai_subdevice_t *instance = dev_id;
+       //int to_read;
+
+       PDEBUG("executed. idx=0\n");
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       irq_status = inl(instance->irq_status_reg);
+       if (!
+           (irq_status &
+            (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC))) {
+#ifdef MEDEBUG_INFO
+               if ((irq_status & (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC | ME4600_IRQ_STATUS_BIT_LE)) == ME4600_IRQ_STATUS_BIT_LE) {   //This is security check case. LE is unused. This should never ever happend.
+                       PINFO
+                           ("%ld Shared interrupt. %s(): irq_status_reg=LE_IRQ\n",
+                            jiffies, __FUNCTION__);
+               } else {
+                       PINFO
+                           ("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n",
+                            jiffies, __FUNCTION__, irq_status);
+               }
+#endif
+               return IRQ_NONE;
+       }
+
+       if (!instance->circ_buf.buf) {  //Security check.
+               PERROR_CRITICAL("CIRCULAR BUFFER NOT EXISTS!\n");
+               ai_stop_isr(instance);
+               return IRQ_HANDLED;
+       }
+       //Get the status register.
+       ctrl_status = inl(instance->status_reg);
+
+#ifdef MEDEBUG_INFO
+       if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF)
+               PINFO("HF interrupt active\n");
+       if (irq_status & ME4600_IRQ_STATUS_BIT_SC)
+               PINFO("SC interrupt active\n");
+       if (irq_status & ME4600_IRQ_STATUS_BIT_LE)
+               PINFO("LE interrupt active\n");
+#endif
+
+       //This is safety check!
+       if ((irq_status & ME4600_IRQ_STATUS_BIT_AI_HF)
+           && (ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA)) {
+               PDEBUG("HF interrupt active but FIFO under half\n");
+               //Reset HF interrupt latch.
+               spin_lock(instance->ctrl_reg_lock);
+               outl(ctrl_status | ME4600_AI_CTRL_BIT_HF_IRQ_RESET,
+                    instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base,
+                          ctrl_status);
+               outl(ctrl_status, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base,
+                          ctrl_status);
+               spin_unlock(instance->ctrl_reg_lock);
+               return IRQ_HANDLED;
+       }
+#ifdef MEDEBUG_INFO
+       PINFO("STATUS_BIT_FSM=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_FSM) ? "on" : "off");
+
+       PINFO("STATUS_BIT_EF_CHANNEL=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_EF_CHANNEL) ? "not empty" :
+             "empty");
+       PINFO("STATUS_BIT_HF_CHANNEL=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_HF_CHANNEL) ? " < HF" :
+             " > HF");
+       PINFO("STATUS_BIT_FF_CHANNEL=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_FF_CHANNEL) ? "not full" :
+             "full");
+
+       PINFO("STATUS_BIT_EF_DATA=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_EF_DATA) ? "not empty" :
+             "empty");
+       PINFO("STATUS_BIT_HF_DATA=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA) ? " < HF" : " > HF");
+       PINFO("STATUS_BIT_FF_DATA=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_FF_DATA) ? "not full" :
+             "full");
+
+       PINFO("CTRL_BIT_HF_IRQ=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_HF_IRQ) ? "enable" : "disable");
+       PINFO("CTRL_BIT_HF_IRQ_RESET=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_HF_IRQ_RESET) ? "reset" :
+             "work");
+       PINFO("CTRL_BIT_SC_IRQ=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ) ? "enable" : "disable");
+       PINFO("CTRL_BIT_SC_RELOAD=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_SC_RELOAD) ? "on" : "off");
+       PINFO("CTRL_BIT_SC_IRQ_RESET=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ_RESET) ? "reset" :
+             "work");
+#endif
+
+       //Look for overflow error.
+       if (!(ctrl_status & ME4600_AI_STATUS_BIT_FF_DATA)) {
+               //FIFO is full. Read datas and reset all settings.
+               PERROR("FIFO overflow.\n");
+               ai_read_data(instance, ME4600_AI_FIFO_COUNT);
+               ai_stop_isr(instance);
+
+               instance->status = ai_status_stream_fifo_error;
+               //Signal it.
+               wake_up_interruptible_all(&instance->wait_queue);
+
+               return IRQ_HANDLED;
+       }
+
+       if (!instance->data_required) { //This is infinite aqusition.
+#ifdef MEDEBUG_ERROR
+               if ((irq_status &
+                    (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC))
+                   ==
+                   (ME4600_IRQ_STATUS_BIT_AI_HF | ME4600_IRQ_STATUS_BIT_SC)) {
+                       ///In infinite mode only one interrupt source should be reported!
+                       PERROR
+                           ("Error in ISM! Infinite aqusition: HF and SC interrupts active! threshold=%d next=%d ctrl=0x%04X irq_status_reg=0x%04X",
+                            instance->fifo_irq_threshold, instance->ISM.next,
+                            ctrl_status, irq_status);
+               }
+#endif
+
+               ai_infinite_isr(instance, irq_status, ctrl_status);
+
+#ifdef MEDEBUG_INFO
+               ctrl_status = inl(instance->ctrl_reg);
+#endif
+       } else {
+
+               ai_limited_isr(instance, irq_status, ctrl_status);
+               ctrl_status = inl(instance->status_reg);
+               if (!(ctrl_status & (ME4600_AI_STATUS_BIT_HF_DATA | ME4600_AI_CTRL_BIT_HF_IRQ_RESET))) {        //HF active, but we have more than half already => HF will never come
+                       PDEBUG
+                           ("MISSED HF. data_required=%d ISM.read=%d ISM.global=%d ISM.next=%d\n",
+                            instance->data_required, instance->ISM.read,
+                            instance->ISM.global_read, instance->ISM.next);
+                       ai_limited_isr(instance, ME4600_IRQ_STATUS_BIT_AI_HF,
+                                      ctrl_status);
+               }
+       }
+
+#ifdef MEDEBUG_INFO
+       PINFO("STATUS_BIT_FSM=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_FSM) ? "on" : "off");
+
+       PINFO("STATUS_BIT_EF_CHANNEL=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_EF_CHANNEL) ? "not empty" :
+             "empty");
+       PINFO("STATUS_BIT_HF_CHANNEL=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_HF_CHANNEL) ? " < HF" :
+             " > HF");
+       PINFO("STATUS_BIT_FF_CHANNEL=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_FF_CHANNEL) ? "not full" :
+             "full");
+
+       PINFO("STATUS_BIT_EF_DATA=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_EF_DATA) ? "not empty" :
+             "empty");
+       PINFO("STATUS_BIT_HF_DATA=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_HF_DATA) ? " < HF" : " > HF");
+       PINFO("STATUS_BIT_FF_DATA=%s.\n",
+             (ctrl_status & ME4600_AI_STATUS_BIT_FF_DATA) ? "not full" :
+             "full");
+
+       PINFO("CTRL_BIT_HF_IRQ_RESET=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_HF_IRQ_RESET) ? "reset" :
+             "work");
+       PINFO("CTRL_BIT_SC_IRQ=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ) ? "enable" : "disable");
+       PINFO("CTRL_BIT_SC_RELOAD=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_SC_RELOAD) ? "on" : "off");
+       PINFO("CTRL_BIT_SC_IRQ_RESET=%s.\n",
+             (ctrl_status & ME4600_AI_CTRL_BIT_SC_IRQ_RESET) ? "reset" :
+             "work");
+       PINFO("%ld END\n", jiffies);
+#endif
+
+       return IRQ_HANDLED;
+}
+
+/** @brief Stop aqusation of data. Reset interrupts' laches. Clear data's FIFO.
+*
+* @param instance The subdevice instance (pointer).
+*/
+void inline ai_stop_isr(me4600_ai_subdevice_t * instance)
+{                              /// @note This is soft time critical function!
+       register uint32_t tmp;
+
+       spin_lock(instance->ctrl_reg_lock);
+       //Stop all. Reset interrupt laches. Reset data FIFO.
+       tmp = inl(instance->ctrl_reg);
+       tmp |=
+           (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP | ME4600_AI_CTRL_BIT_HF_IRQ_RESET
+            | ME4600_AI_CTRL_BIT_LE_IRQ_RESET |
+            ME4600_AI_CTRL_BIT_SC_IRQ_RESET);
+       tmp &= ~ME4600_AI_CTRL_BIT_DATA_FIFO;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->ctrl_reg_lock);
+}
+
+/** @brief Copy data from fifo to circular buffer.
+*
+* @param instance The subdevice instance (pointer).
+* @param count The number of requested data.
+*
+* @return On success: Number of copied values.
+* @return On error: -ME_ERRNO_RING_BUFFER_OVERFLOW.
+*/
+static int inline ai_read_data(me4600_ai_subdevice_t * instance,
+                              const int count)
+{                              /// @note This is time critical function!
+       int c = count;
+       int empty_space;
+       int copied = 0;
+       int i, j;
+
+       empty_space = me_circ_buf_space_to_end(&instance->circ_buf);
+       if (empty_space <= 0) {
+               PDEBUG("Circular buffer full.\n");
+               return -ME_ERRNO_RING_BUFFER_OVERFLOW;
+       }
+
+       if (empty_space < c) {  //Copy first part. Max to end of buffer.
+               PDEBUG
+                   ("Try to copy %d values from FIFO to circular buffer (pass 1).\n",
+                    empty_space);
+               for (i = 0; i < empty_space; i++) {
+                       *(instance->circ_buf.buf + instance->circ_buf.head) =
+                           (inw(instance->data_reg) ^ 0x8000);
+                       instance->circ_buf.head++;
+               }
+               instance->circ_buf.head &= instance->circ_buf.mask;
+               c -= empty_space;
+               copied = empty_space;
+
+               empty_space = me_circ_buf_space_to_end(&instance->circ_buf);
+       }
+
+       if (empty_space > 0) {
+               j = (empty_space < c) ? empty_space : c;
+               PDEBUG
+                   ("Try to copy %d values from FIFO to circular buffer (pass 2).\n",
+                    c);
+               for (i = 0; i < j; i++) {
+                       *(instance->circ_buf.buf + instance->circ_buf.head) =
+                           (inw(instance->data_reg) ^ 0x8000);
+                       instance->circ_buf.head++;
+               }
+               instance->circ_buf.head &= instance->circ_buf.mask;
+               copied += j;
+       }
+       return copied;
+}
+
+void inline ai_infinite_ISM(me4600_ai_subdevice_t * instance)
+{                              /// @note This is time critical function!
+       register volatile uint32_t ctrl_set, ctrl_reset, tmp;
+
+       if (instance->fifo_irq_threshold < ME4600_AI_FIFO_MAX_SC) {     // Only sample counter with reloadnig is working. Reset it.
+               PINFO
+                   ("Only sample counter with reloadnig is working. Reset it.\n");
+               ctrl_set = ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+               ctrl_reset = ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+       } else if (instance->fifo_irq_threshold == instance->ISM.read) {        //This is SC interrupt for large block. The whole section is done. Reset SC_IRQ an HF_IRQ and start everything again from beginning.
+               PINFO
+                   ("This is SC interrupt for large block. The whole section is done. Reset SC_IRQ an HF_IRQ and start everything again from beginning.\n");
+               ctrl_set =
+                   ME4600_AI_CTRL_BIT_SC_IRQ_RESET |
+                   ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+               ctrl_reset =
+                   ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET |
+                     ME4600_AI_CTRL_BIT_HF_IRQ_RESET);
+       } else if (instance->fifo_irq_threshold >= (ME4600_AI_FIFO_MAX_SC + instance->ISM.read)) {      //This is HF interrupt for large block.The next interrupt should be from HF, also. Reset HF.
+               PINFO
+                   ("This is HF interrupt for large block.The next interrupt should be from HF, also. Reset HF.\n");
+               ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+               ctrl_reset = ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+       } else {                //This is HF interrupt for large block.The next interrupt should be from SC. Don't reset HF!
+               PINFO
+                   ("This is HF interrupt for large block.The next interrupt should be from SC. Don't reset HF!\n");
+               ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+               ctrl_reset = 0xFFFFFFFF;
+       }
+
+       //Reset interrupt latch.
+       spin_lock(instance->ctrl_reg_lock);
+       tmp = inl(instance->ctrl_reg);
+       PINFO("ctrl=0x%x ctrl_set=0x%x ctrl_reset=0x%x\n", tmp, ctrl_set,
+             ctrl_reset);
+       tmp |= ctrl_set;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       if (ctrl_reset != 0xFFFFFFFF) {
+               outl(tmp & ctrl_reset, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reset outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base,
+                          tmp & ctrl_reset);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+
+}
+
+void inline ai_limited_ISM(me4600_ai_subdevice_t * instance,
+                          uint32_t irq_status)
+{                              /// @note This is time critical function!
+       register volatile uint32_t ctrl_set, ctrl_reset = 0xFFFFFFFF, tmp;
+
+       if (!instance->fifo_irq_threshold) {    //No threshold provided. SC ends work.
+               PINFO("No threshold provided. SC ends work.\n");
+               ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+               if (instance->data_required > (ME4600_AI_FIFO_COUNT - 1 + instance->ISM.global_read)) { //HF need reseting.
+                       ctrl_reset &= ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+               }
+       } else                  //if(instance->fifo_irq_threshold)
+       {
+               if (irq_status & ME4600_IRQ_STATUS_BIT_AI_HF) {
+                       PINFO("Threshold provided. Clear HF latch.\n");
+                       ctrl_set = ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+
+                       if (instance->fifo_irq_threshold >= (ME4600_AI_FIFO_MAX_SC + instance->ISM.read)) {     //This is not the last one. HF need reseting.
+                               PINFO
+                                   ("The next interrupt is HF. HF need be activating.\n");
+                               ctrl_reset = ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+                       }
+               }
+
+               if (irq_status & ME4600_IRQ_STATUS_BIT_SC) {
+                       PINFO("Threshold provided. Restart SC.\n");
+                       ctrl_set = ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+                       ctrl_reset &= ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+
+                       if (instance->fifo_irq_threshold >= ME4600_AI_FIFO_MAX_SC) {    //This is not the last one. HF need to be activating.
+                               PINFO
+                                   ("The next interrupt is HF. HF need to be activating.\n");
+                               ctrl_reset &= ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+                       }
+               }
+       }
+
+       //Reset interrupt latch.
+       spin_lock(instance->ctrl_reg_lock);
+       tmp = inl(instance->ctrl_reg);
+       tmp |= ctrl_set;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       if (ctrl_reset != 0xFFFFFFFF) {
+               outl(tmp & ctrl_reset, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base,
+                          tmp & ctrl_reset);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+
+}
+
+/** @brief Last chunck of datas. We must reschedule sample counter.
+*      @note Last chunck.
+*      Leaving SC_RELOAD doesn't do any harm, but in some bad case can make extra interrupts.
+*      @warning When threshold is wrongly set some IRQ are lost.(!!!)
+*/
+void inline ai_reschedule_SC(me4600_ai_subdevice_t * instance)
+{
+       register uint32_t rest;
+
+       if (instance->data_required <= instance->ISM.global_read)
+               return;
+
+       rest = instance->data_required - instance->ISM.global_read;
+       if (rest < instance->fifo_irq_threshold) {      //End of work soon ....
+               PDEBUG("Rescheduling SC from %d to %d.\n",
+                      instance->fifo_irq_threshold, rest);
+               /// @note Write new value to SC <==  DANGER! This is not safe solution! We can miss some inputs.
+               outl(rest, instance->sample_counter_reg);
+               PDEBUG_REG("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->sample_counter_reg - instance->reg_base,
+                          rest);
+               instance->fifo_irq_threshold = rest;
+
+               if (rest < ME4600_AI_FIFO_MAX_SC) {
+                       instance->ISM.next = rest;
+               } else {
+                       instance->ISM.next = rest % ME4600_AI_FIFO_HALF;
+                       if (instance->ISM.next + ME4600_AI_FIFO_HALF <
+                           ME4600_AI_FIFO_MAX_SC) {
+                               instance->ISM.next += ME4600_AI_FIFO_HALF;
+                       }
+               }
+       }
+}
+
+/** Start the ISM. All must be reseted before enter to this function. */
+void inline ai_data_acquisition_logic(me4600_ai_subdevice_t * instance)
+{
+       register uint32_t tmp;
+
+       if (!instance->data_required) { //This is infinite aqusition.
+               if (!instance->fifo_irq_threshold) {    //No threshold provided. Set SC to 0.5*FIFO. Clear the SC's latch.
+                       //Set the sample counter
+                       outl(ME4600_AI_FIFO_HALF, instance->sample_counter_reg);
+                       PDEBUG_REG
+                           ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n",
+                            instance->reg_base,
+                            instance->sample_counter_reg - instance->reg_base,
+                            ME4600_AI_FIFO_HALF);
+               } else {        //Threshold provided. Set SC to treshold. Clear the SC's latch.
+                       //Set the sample counter
+                       outl(instance->fifo_irq_threshold,
+                            instance->sample_counter_reg);
+                       PDEBUG_REG
+                           ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n",
+                            instance->reg_base,
+                            instance->sample_counter_reg - instance->reg_base,
+                            instance->fifo_irq_threshold);
+               }
+
+               if (instance->fifo_irq_threshold < ME4600_AI_FIFO_MAX_SC) {     //Enable only sample counter's interrupt. Set reload bit. Clear the SC's latch.
+                       spin_lock(instance->ctrl_reg_lock);
+                       tmp = inl(instance->ctrl_reg);
+                       tmp |= ME4600_AI_CTRL_BIT_SC_RELOAD;
+                       tmp &= ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+                       outl(tmp, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  tmp);
+                       spin_unlock(instance->ctrl_reg_lock);
+                       if (!instance->fifo_irq_threshold) {    //No threshold provided. Set ISM.next to 0.5*FIFO.
+                               instance->ISM.next = ME4600_AI_FIFO_HALF;
+                       } else {        //Threshold provided. Set ISM.next to treshold.
+                               instance->ISM.next =
+                                   instance->fifo_irq_threshold;
+                       }
+               } else {        //Enable sample counter's and HF's interrupts.
+                       spin_lock(instance->ctrl_reg_lock);
+                       tmp = inl(instance->ctrl_reg);
+                       tmp |= ME4600_AI_CTRL_BIT_SC_RELOAD;
+                       tmp &=
+                           ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET |
+                             ME4600_AI_CTRL_BIT_HF_IRQ_RESET);
+                       outl(tmp, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  tmp);
+                       spin_unlock(instance->ctrl_reg_lock);
+
+                       instance->ISM.next =
+                           instance->fifo_irq_threshold % ME4600_AI_FIFO_HALF;
+                       if (instance->ISM.next + ME4600_AI_FIFO_HALF <
+                           ME4600_AI_FIFO_MAX_SC) {
+                               instance->ISM.next += ME4600_AI_FIFO_HALF;
+                       }
+               }
+       } else {                //This aqusition is limited to set number of data.
+               if (instance->fifo_irq_threshold >= instance->data_required) {  //Stupid situation.
+                       instance->fifo_irq_threshold = 0;
+                       PDEBUG
+                           ("Stupid situation: data_required(%d) < threshold(%d).\n",
+                            instance->fifo_irq_threshold,
+                            instance->data_required);
+               }
+
+               if (!instance->fifo_irq_threshold) {    //No threshold provided. Easy case: HF=read and SC=end.
+                       //Set the sample counter to data_required.
+                       outl(instance->data_required,
+                            instance->sample_counter_reg);
+                       PDEBUG_REG
+                           ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n",
+                            instance->reg_base,
+                            instance->sample_counter_reg - instance->reg_base,
+                            instance->data_required);
+
+                       //Reset the latches of sample counter and HF (if SC>FIFO).
+                       //No SC reload!
+                       spin_lock(instance->ctrl_reg_lock);
+                       tmp = inl(instance->ctrl_reg);
+                       tmp &=
+                           ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET |
+                             ME4600_AI_CTRL_BIT_SC_RELOAD);
+                       if (instance->data_required >
+                           (ME4600_AI_FIFO_COUNT - 1)) {
+                               tmp &= ~ME4600_AI_CTRL_BIT_HF_IRQ_RESET;
+                               instance->ISM.next =
+                                   instance->data_required %
+                                   ME4600_AI_FIFO_HALF;
+                               instance->ISM.next += ME4600_AI_FIFO_HALF;
+
+                       } else {
+                               instance->ISM.next = instance->data_required;
+                       }
+                       outl(tmp, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  tmp);
+                       spin_unlock(instance->ctrl_reg_lock);
+
+               } else {        //The most general case. We have concret numbe of required data and threshold. SC=TH
+                       //Set the sample counter to threshold.
+                       outl(instance->fifo_irq_threshold,
+                            instance->sample_counter_reg);
+                       PDEBUG_REG
+                           ("sample_counter_reg outl(0x%lX+0x%lX)=0x%x\n",
+                            instance->reg_base,
+                            instance->sample_counter_reg - instance->reg_base,
+                            instance->fifo_irq_threshold);
+
+                       spin_lock(instance->ctrl_reg_lock);
+                       tmp = inl(instance->ctrl_reg);
+                       //In this moment we are sure that SC will come more than once.
+                       tmp |= ME4600_AI_CTRL_BIT_SC_RELOAD;
+
+                       if (instance->fifo_irq_threshold < ME4600_AI_FIFO_MAX_SC) {     //The threshold is so small that we do need HF.
+                               tmp &= ~ME4600_AI_CTRL_BIT_SC_IRQ_RESET;
+                               instance->ISM.next =
+                                   instance->fifo_irq_threshold;
+                       } else {        //The threshold is large. The HF must be use.
+                               tmp &=
+                                   ~(ME4600_AI_CTRL_BIT_SC_IRQ_RESET |
+                                     ME4600_AI_CTRL_BIT_HF_IRQ_RESET);
+                               instance->ISM.next =
+                                   instance->fifo_irq_threshold %
+                                   ME4600_AI_FIFO_HALF;
+                               if (instance->ISM.next + ME4600_AI_FIFO_HALF <
+                                   ME4600_AI_FIFO_MAX_SC) {
+                                       instance->ISM.next +=
+                                           ME4600_AI_FIFO_HALF;
+                               }
+                       }
+                       outl(tmp, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  tmp);
+                       spin_unlock(instance->ctrl_reg_lock);
+               }
+       }
+}
+
+static int ai_mux_toggler(me4600_ai_subdevice_t * instance)
+{
+       uint32_t tmp;
+
+       PDEBUG("executed. idx=0\n");
+
+       outl(0, instance->scan_pre_timer_low_reg);
+       PDEBUG_REG("scan_pre_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_low_reg - instance->reg_base, 0);
+       outl(0, instance->scan_pre_timer_high_reg);
+       PDEBUG_REG("scan_pre_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_pre_timer_high_reg - instance->reg_base, 0);
+       outl(0, instance->scan_timer_low_reg);
+       PDEBUG_REG("scan_timer_low_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_timer_low_reg - instance->reg_base, 0);
+       outl(0, instance->scan_timer_high_reg);
+       PDEBUG_REG("scan_timer_high_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->scan_timer_high_reg - instance->reg_base, 0);
+       outl(65, instance->chan_timer_reg);
+       PDEBUG_REG("chan_timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->chan_timer_reg - instance->reg_base, 65);
+       outl(65, instance->chan_pre_timer_reg);
+       PDEBUG_REG("chan_pre_timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->chan_pre_timer_reg - instance->reg_base, 65);
+
+       // Turn on internal reference.
+       tmp = inl(instance->ctrl_reg);
+       tmp |= ME4600_AI_CTRL_BIT_FULLSCALE;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       // Clear data and channel fifo.
+       tmp &=
+           ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       tmp |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       // Write channel entry.
+       outl(ME4600_AI_LIST_INPUT_DIFFERENTIAL |
+            ME4600_AI_LIST_RANGE_UNIPOLAR_2_5 | 31,
+            instance->channel_list_reg);
+       PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->channel_list_reg - instance->reg_base,
+                  ME4600_AI_LIST_INPUT_DIFFERENTIAL |
+                  ME4600_AI_LIST_RANGE_UNIPOLAR_2_5 | 31);
+
+       // Start conversion.
+       inl(instance->start_reg);
+       PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base,
+                  instance->start_reg - instance->reg_base);
+       udelay(10);
+
+       // Clear data and channel fifo.
+       tmp &=
+           ~(ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       tmp |= ME4600_AI_CTRL_BIT_CHANNEL_FIFO | ME4600_AI_CTRL_BIT_DATA_FIFO;
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       // Write channel entry.
+       // ME4600_AI_LIST_INPUT_SINGLE_ENDED | ME4600_AI_LIST_RANGE_BIPOLAR_10 <= 0x0000
+       outl(ME4600_AI_LIST_INPUT_SINGLE_ENDED |
+            ME4600_AI_LIST_RANGE_BIPOLAR_10, instance->channel_list_reg);
+       PDEBUG_REG("channel_list_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->channel_list_reg - instance->reg_base,
+                  ME4600_AI_LIST_INPUT_SINGLE_ENDED |
+                  ME4600_AI_LIST_RANGE_BIPOLAR_10);
+
+       // Start conversion.
+       inl(instance->start_reg);
+       PDEBUG_REG("start_reg inl(0x%lX+0x%lX)\n", instance->reg_base,
+                  instance->start_reg - instance->reg_base);
+       udelay(10);
+
+       // Clear control register.
+       tmp &= (ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+/** @brief Copy rest of data from fifo to circular buffer.
+* @note Helper for STOP command. After FSM is stopped.
+* @note This is slow function that copy all remainig data from FIFO to buffer.
+*
+* @param instance The subdevice instance (pointer).
+*
+* @return On success: Number of copied values.
+* @return On error: Negative error code -ME_ERRNO_RING_BUFFER_OVERFLOW.
+*/
+static int inline ai_read_data_pooling(me4600_ai_subdevice_t * instance)
+{                              /// @note This is time critical function!
+       int empty_space;
+       int copied = 0;
+       int status = ME_ERRNO_SUCCESS;
+
+       PDEBUG("Space left in circular buffer = %d.\n",
+              me_circ_buf_space(&instance->circ_buf));
+
+       while ((empty_space = me_circ_buf_space(&instance->circ_buf))) {
+               if (!(status = inl(instance->status_reg) & ME4600_AI_STATUS_BIT_EF_DATA)) {     //No more data. status = ME_ERRNO_SUCCESS = 0
+                       break;
+               }
+               *(instance->circ_buf.buf + instance->circ_buf.head) =
+                   (inw(instance->data_reg) ^ 0x8000);
+               instance->circ_buf.head++;
+               instance->circ_buf.head &= instance->circ_buf.mask;
+       }
+
+#ifdef MEDEBUG_ERROR
+       if (!status)
+               PDEBUG
+                   ("Copied all remaining datas (%d) from FIFO to circular buffer.\n",
+                    copied);
+       else {
+               PDEBUG("No more empty space in buffer.\n");
+               PDEBUG("Copied %d datas from FIFO to circular buffer.\n",
+                      copied);
+               PDEBUG("FIFO still not empty.\n");
+       }
+#endif
+       return (!status) ? copied : -ME_ERRNO_RING_BUFFER_OVERFLOW;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static void me4600_ai_work_control_task(void *subdevice)
+#else
+static void me4600_ai_work_control_task(struct work_struct *work)
+#endif
+{
+       me4600_ai_subdevice_t *instance;
+       uint32_t status;
+       uint32_t ctrl;
+       unsigned long cpu_flags = 0;
+       int reschedule = 0;
+       int signaling = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       instance = (me4600_ai_subdevice_t *) subdevice;
+#else
+       instance =
+           container_of((void *)work, me4600_ai_subdevice_t, ai_control_task);
+#endif
+       PINFO("<%s: %ld> executed.\n", __FUNCTION__, jiffies);
+
+       status = inl(instance->status_reg);
+       PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->status_reg - instance->reg_base, status);
+
+       switch (instance->status) {     // Checking actual mode.
+               // Not configured for work.
+       case ai_status_none:
+               break;
+
+               //This are stable modes. No need to do anything. (?)
+       case ai_status_single_configured:
+       case ai_status_stream_configured:
+       case ai_status_stream_fifo_error:
+       case ai_status_stream_buffer_error:
+       case ai_status_stream_error:
+               PERROR("Shouldn't be running!.\n");
+               break;
+
+               // Stream modes
+       case ai_status_stream_run_wait:
+               if (status & ME4600_AI_STATUS_BIT_FSM) {        // ISM started..
+                       instance->status = ai_status_stream_run;
+                       // Signal the end of wait for start.
+                       signaling = 1;
+                       // Wait now for stop.
+                       reschedule = 1;
+                       break;
+
+                       // Check timeout.
+                       if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {       // Timeout
+                               PDEBUG("Timeout reached.\n");
+                               // Stop all actions. No conditions! Block interrupts. Reset FIFO => Too late!
+                               ai_stop_isr(instance);
+
+                               instance->status = ai_status_stream_end;
+
+                               // Signal the end.
+                               signaling = 1;
+                       }
+               }
+               break;
+
+       case ai_status_stream_run:
+               // Wait for stop ISM.
+               reschedule = 1;
+               break;
+
+       case ai_status_stream_end_wait:
+               if (!(status & ME4600_AI_STATUS_BIT_FSM)) {     // ISM stoped. Overwrite ISR.
+                       instance->status = ai_status_stream_end;
+                       // Signal the end of wait for stop.
+                       signaling = 1;
+               } else {
+                       // Wait for stop ISM.
+                       reschedule = 1;
+               }
+               break;
+
+       case ai_status_stream_end:
+               //End work.
+               if (status & ME4600_AI_STATUS_BIT_FSM) {        // Still working? Stop it!
+                       PERROR
+                           ("Status is 'ai_status_stream_end' but hardware is still working!\n");
+                       spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           (ME4600_AI_CTRL_BIT_IMMEDIATE_STOP |
+                            ME4600_AI_CTRL_BIT_HF_IRQ_RESET |
+                            ME4600_AI_CTRL_BIT_SC_IRQ_RESET);
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(instance->ctrl_reg_lock,
+                                              cpu_flags);
+               }
+               break;
+
+       default:
+               PERROR_CRITICAL("Status is in wrong state (%d)!\n",
+                               instance->status);
+               instance->status = ai_status_stream_error;
+               // Signal the end.
+               signaling = 1;
+               break;
+
+       }
+
+       if (signaling) {        //Signal it.
+               wake_up_interruptible_all(&instance->wait_queue);
+       }
+
+       if (instance->ai_control_task_flag && reschedule) {     // Reschedule task
+               queue_delayed_work(instance->me4600_workqueue,
+                                  &instance->ai_control_task, 1);
+       } else {
+               PINFO("<%s> Ending control task.\n", __FUNCTION__);
+       }
+
+}
diff --git a/drivers/staging/meilhaus/me4600_ai.h b/drivers/staging/meilhaus/me4600_ai.h
new file mode 100644 (file)
index 0000000..1d5a1b9
--- /dev/null
@@ -0,0 +1,180 @@
+/**
+ * @file me4600_ai.h
+ *
+ * @brief Meilhaus ME-4000 analog input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke  (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_AI_H_
+#define _ME4600_AI_H_
+
+#include <linux/version.h>
+#include "mesubdevice.h"
+#include "meioctl.h"
+#include "mecirc_buf.h"
+
+#ifdef __KERNEL__
+
+#define ME4600_AI_MAX_DATA                             0xFFFF
+
+#ifdef ME_SYNAPSE
+# define ME4600_AI_CIRC_BUF_SIZE_ORDER 8       // 2^n PAGES =>> Maximum value of 1MB for Synapse
+#else
+# define ME4600_AI_CIRC_BUF_SIZE_ORDER 5       // 2^n PAGES =>> 128KB
+#endif
+#define ME4600_AI_CIRC_BUF_SIZE                PAGE_SIZE<<ME4600_AI_CIRC_BUF_SIZE_ORDER        // Buffer size in bytes.
+
+#ifdef _CBUFF_32b_t
+# define ME4600_AI_CIRC_BUF_COUNT              ((ME4600_AI_CIRC_BUF_SIZE) / sizeof(uint32_t))  // Size in values
+#else
+# define ME4600_AI_CIRC_BUF_COUNT              ((ME4600_AI_CIRC_BUF_SIZE) / sizeof(uint16_t))  // Size in values
+#endif
+
+#define ME4600_AI_FIFO_HALF                            1024    //ME4600_AI_FIFO_COUNT/2                //1024
+#define ME4600_AI_FIFO_MAX_SC                  1352    //0.66*ME4600_AI_FIFO_COUNT             //1352
+
+typedef enum ME4600_AI_STATUS {
+       ai_status_none = 0,
+       ai_status_single_configured,
+       ai_status_stream_configured,
+       ai_status_stream_run_wait,
+       ai_status_stream_run,
+       ai_status_stream_end_wait,
+       ai_status_stream_end,
+       ai_status_stream_fifo_error,
+       ai_status_stream_buffer_error,
+       ai_status_stream_error,
+       ai_status_last
+} ME4600_AI_STATUS;
+
+typedef struct me4600_single_config_entry {
+       unsigned short status;
+       uint32_t entry;
+       uint32_t ctrl;
+} me4600_single_config_entry_t;
+
+typedef struct me4600_range_entry {
+       int min;
+       int max;
+} me4600_range_entry_t;
+
+typedef struct me4600_ai_ISM {
+       volatile unsigned int global_read;                              /**< The number of data read in total. */
+       volatile unsigned int read;                                             /**< The number of data read for this chunck. */
+       volatile unsigned int next;                                             /**< The number of data request by user. */
+} me4600_ai_ISM_t;
+
+typedef struct me4600_ai_timeout {
+       unsigned long start_time;
+       unsigned long delay;
+} me4600_ai_timeout_t;
+
+/**
+ * @brief The ME-4000 analog input subdevice class.
+ */
+typedef struct me4600_ai_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                                                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;                                              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;                                              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+
+       /* Hardware feautres */
+       unsigned int irq;                                                               /**< The interrupt request number assigned by the PCI BIOS. */
+       int isolated;                                                                   /**< Marks if this subdevice is on an optoisolated device. */
+       int sh;                                                                                 /**< Marks if this subdevice has sample and hold devices. */
+
+       unsigned int channels;                                                  /**< The number of channels available on this subdevice. */
+       me4600_single_config_entry_t single_config[32]; /**< The configuration set for single acquisition. */
+
+       unsigned int data_required;                                             /**< The number of data request by user. */
+       unsigned int fifo_irq_threshold;                                /**< The user adjusted FIFO high water interrupt level. */
+       unsigned int chan_list_len;                                             /**< The length of the user defined channel list. */
+
+       me4600_ai_ISM_t ISM;                                                    /**< The information request by Interrupt-State-Machine. */
+       volatile enum ME4600_AI_STATUS status;                  /**< The current stream status flag. */
+       me4600_ai_timeout_t timeout;                                    /**< The timeout for start in blocking and non-blocking mode. */
+
+                                                                                       /* Registers *//**< All registers are 32 bits long. */
+       unsigned long ctrl_reg;
+       unsigned long status_reg;
+       unsigned long channel_list_reg;
+       unsigned long data_reg;
+       unsigned long chan_timer_reg;
+       unsigned long chan_pre_timer_reg;
+       unsigned long scan_timer_low_reg;
+       unsigned long scan_timer_high_reg;
+       unsigned long scan_pre_timer_low_reg;
+       unsigned long scan_pre_timer_high_reg;
+       unsigned long start_reg;
+       unsigned long irq_status_reg;
+       unsigned long sample_counter_reg;
+
+       unsigned int ranges_len;
+       me4600_range_entry_t ranges[4];                                 /**< The ranges available on this subdevice. */
+
+       /* Software buffer */
+       me_circ_buf_t circ_buf;                                                 /**< Circular buffer holding measurment data. */
+       wait_queue_head_t wait_queue;                                   /**< Wait queue to put on tasks waiting for data to arrive. */
+
+       struct workqueue_struct *me4600_workqueue;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       struct work_struct ai_control_task;
+#else
+       struct delayed_work ai_control_task;
+#endif
+
+       volatile int ai_control_task_flag;                              /**< Flag controling reexecuting of control task */
+
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me4600_ai_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-4000 analog input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param channels The number of analog input channels available on this subdevice.
+ * @param channels The number of analog input ranges available on this subdevice.
+ * @param isolated Flag indicating if this device is opto isolated.
+ * @param sh Flag indicating if sample and hold devices are available.
+ * @param irq The irq number assigned by PCI BIOS.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me4600_ai_subdevice_t *me4600_ai_constructor(uint32_t reg_base,
+                                            unsigned int channels,
+                                            unsigned int ranges,
+                                            int isolated,
+                                            int sh,
+                                            int irq,
+                                            spinlock_t * ctrl_reg_lock,
+                                            struct workqueue_struct
+                                            *me4600_wq);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_ai_reg.h b/drivers/staging/meilhaus/me4600_ai_reg.h
new file mode 100644 (file)
index 0000000..083fac7
--- /dev/null
@@ -0,0 +1,107 @@
+/**
+ * @file me4600_ai_reg.h
+ *
+ * @brief ME-4000 analog input subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_AI_REG_H_
+#define _ME4600_AI_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME4600_AI_CTRL_REG                                     0x74    // _/W
+#define ME4600_AI_STATUS_REG                           0x74    // R/_
+#define ME4600_AI_CHANNEL_LIST_REG                     0x78    // _/W
+#define ME4600_AI_DATA_REG                                     0x7C    // R/_
+#define ME4600_AI_CHAN_TIMER_REG                       0x80    // _/W
+#define ME4600_AI_CHAN_PRE_TIMER_REG           0x84    // _/W
+#define ME4600_AI_SCAN_TIMER_LOW_REG           0x88    // _/W
+#define ME4600_AI_SCAN_TIMER_HIGH_REG          0x8C    // _/W
+#define ME4600_AI_SCAN_PRE_TIMER_LOW_REG       0x90    // _/W
+#define ME4600_AI_SCAN_PRE_TIMER_HIGH_REG      0x94    // _/W
+#define ME4600_AI_START_REG                                    0x98    // R/_
+
+#define ME4600_AI_SAMPLE_COUNTER_REG           0xC0    // _/W
+
+#define ME4600_AI_CTRL_BIT_MODE_0                      0x00000001
+#define ME4600_AI_CTRL_BIT_MODE_1                      0x00000002
+#define ME4600_AI_CTRL_BIT_MODE_2                      0x00000004
+#define ME4600_AI_CTRL_BIT_SAMPLE_HOLD         0x00000008
+#define ME4600_AI_CTRL_BIT_IMMEDIATE_STOP      0x00000010
+#define ME4600_AI_CTRL_BIT_STOP                                0x00000020
+#define ME4600_AI_CTRL_BIT_CHANNEL_FIFO                0x00000040
+#define ME4600_AI_CTRL_BIT_DATA_FIFO           0x00000080
+#define ME4600_AI_CTRL_BIT_FULLSCALE           0x00000100
+#define ME4600_AI_CTRL_BIT_OFFSET                      0x00000200
+#define ME4600_AI_CTRL_BIT_EX_TRIG_ANALOG      0x00000400
+#define ME4600_AI_CTRL_BIT_EX_TRIG                     0x00000800
+#define ME4600_AI_CTRL_BIT_EX_TRIG_FALLING     0x00001000
+#define ME4600_AI_CTRL_BIT_EX_IRQ                      0x00002000
+#define ME4600_AI_CTRL_BIT_EX_IRQ_RESET                0x00004000
+#define ME4600_AI_CTRL_BIT_LE_IRQ                      0x00008000
+#define ME4600_AI_CTRL_BIT_LE_IRQ_RESET                0x00010000
+#define ME4600_AI_CTRL_BIT_HF_IRQ                      0x00020000
+#define ME4600_AI_CTRL_BIT_HF_IRQ_RESET                0x00040000
+#define ME4600_AI_CTRL_BIT_SC_IRQ                      0x00080000
+#define ME4600_AI_CTRL_BIT_SC_IRQ_RESET                0x00100000
+#define ME4600_AI_CTRL_BIT_SC_RELOAD           0x00200000
+#define ME4600_AI_CTRL_BIT_EX_TRIG_BOTH                0x80000000
+
+#define ME4600_AI_STATUS_BIT_EF_CHANNEL                0x00400000
+#define ME4600_AI_STATUS_BIT_HF_CHANNEL                0x00800000
+#define ME4600_AI_STATUS_BIT_FF_CHANNEL                0x01000000
+#define ME4600_AI_STATUS_BIT_EF_DATA           0x02000000
+#define ME4600_AI_STATUS_BIT_HF_DATA           0x04000000
+#define ME4600_AI_STATUS_BIT_FF_DATA           0x08000000
+#define ME4600_AI_STATUS_BIT_LE                                0x10000000
+#define ME4600_AI_STATUS_BIT_FSM                       0x20000000
+
+#define ME4600_AI_CTRL_RPCI_FIFO                       0x40000000      //Always set to zero!
+
+#define ME4600_AI_BASE_FREQUENCY                       33E6
+
+#define ME4600_AI_MIN_ACQ_TICKS                                66LL
+#define ME4600_AI_MAX_ACQ_TICKS                                0xFFFFFFFFLL
+
+#define ME4600_AI_MIN_SCAN_TICKS                       66LL
+#define ME4600_AI_MAX_SCAN_TICKS                       0xFFFFFFFFFLL
+
+#define ME4600_AI_MIN_CHAN_TICKS                       66LL
+#define ME4600_AI_MAX_CHAN_TICKS                       0xFFFFFFFFLL
+
+#define ME4600_AI_FIFO_COUNT                           2048
+
+#define ME4600_AI_LIST_COUNT                           1024
+
+#define ME4600_AI_LIST_INPUT_SINGLE_ENDED      0x000
+#define ME4600_AI_LIST_INPUT_DIFFERENTIAL      0x020
+
+#define ME4600_AI_LIST_RANGE_BIPOLAR_10                0x000
+#define ME4600_AI_LIST_RANGE_BIPOLAR_2_5       0x040
+#define ME4600_AI_LIST_RANGE_UNIPOLAR_10       0x080
+#define ME4600_AI_LIST_RANGE_UNIPOLAR_2_5      0x0C0
+
+#define ME4600_AI_LIST_LAST_ENTRY              0x100
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_ao.c b/drivers/staging/meilhaus/me4600_ao.c
new file mode 100644 (file)
index 0000000..2c92e65
--- /dev/null
@@ -0,0 +1,6011 @@
+/**
+ * @file me4600_ao.c
+ *
+ * @brief ME-4000 analog output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+///Common part. (For normal and Bosch builds.)
+
+/* Includes
+ */
+
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "meids.h"
+#include "me4600_reg.h"
+#include "me4600_ao_reg.h"
+#include "me4600_ao.h"
+
+/* Defines
+ */
+
+static int me4600_ao_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range);
+
+static int me4600_ao_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count);
+
+static int me4600_ao_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata);
+
+static int me4600_ao_query_timer(me_subdevice_t * subdevice,
+                                int timer,
+                                int *base_frequency,
+                                long long *min_ticks, long long *max_ticks);
+
+static int me4600_ao_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number);
+
+static int me4600_ao_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype);
+
+static int me4600_ao_query_subdevice_caps(me_subdevice_t * subdevice,
+                                         int *caps);
+
+static int me4600_ao_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                              int cap, int *args, int count);
+
+#ifndef BOSCH
+/// @note NORMAL BUILD
+/// @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+/* Includes
+ */
+
+# include <linux/workqueue.h>
+
+/* Defines
+ */
+
+/** Remove subdevice.
+*/
+static void me4600_ao_destructor(struct me_subdevice *subdevice);
+
+/** Reset subdevice. Stop all actions. Reset registry. Disable FIFO. Set output to 0V and status to 'none'.
+*/
+static int me4600_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags);
+
+/** Set output as single
+*/
+static int me4600_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags);
+
+/** Pass to user actual value of output.
+*/
+static int me4600_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags);
+
+/** Write to output requed value.
+*/
+static int me4600_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags);
+
+/** Set output as streamed device.
+*/
+static int me4600_ao_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags);
+
+/** Wait for / Check empty space in buffer.
+*/
+static int me4600_ao_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags);
+
+/** Start streaming.
+*/
+static int me4600_ao_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags);
+
+/** Check actual state. / Wait for end.
+*/
+static int me4600_ao_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags);
+
+/** Stop streaming.
+*/
+static int me4600_ao_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags);
+
+/** Write datas to buffor.
+*/
+static int me4600_ao_io_stream_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int write_mode,
+                                    int *values, int *count, int flags);
+
+/** Interrupt handler. Copy from buffer to FIFO.
+*/
+static irqreturn_t me4600_ao_isr(int irq, void *dev_id
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+                                , struct pt_regs *regs
+#endif
+    );
+/** Copy data from circular buffer to fifo (fast) in wraparound mode.
+*/
+int inline ao_write_data_wraparound(me4600_ao_subdevice_t * instance, int count,
+                                   int start_pos);
+
+/** Copy data from circular buffer to fifo (fast).
+*/
+int inline ao_write_data(me4600_ao_subdevice_t * instance, int count,
+                        int start_pos);
+
+/** Copy data from circular buffer to fifo (slow).
+*/
+int inline ao_write_data_pooling(me4600_ao_subdevice_t * instance, int count,
+                                int start_pos);
+
+/** Copy data from user space to circular buffer.
+*/
+int inline ao_get_data_from_user(me4600_ao_subdevice_t * instance, int count,
+                                int *user_values);
+
+/** Stop presentation. Preserve FIFOs.
+*/
+int inline ao_stop_immediately(me4600_ao_subdevice_t * instance);
+
+/** Task for asynchronical state verifying.
+*/
+static void me4600_ao_work_control_task(
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+                                              void *subdevice
+#else
+                                              struct work_struct *work
+#endif
+    );
+/* Functions
+ */
+
+static int me4600_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t tmp;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       instance->status = ao_status_none;
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+       instance->timeout.delay = 0;
+       instance->timeout.start_time = jiffies;
+
+       //Stop state machine.
+       err = ao_stop_immediately(instance);
+
+       //Remove from synchronous start.
+       spin_lock(instance->preload_reg_lock);
+       tmp = inl(instance->preload_reg);
+       tmp &=
+           ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->
+             ao_idx);
+       outl(tmp, instance->preload_reg);
+       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, tmp);
+       *instance->preload_flags &=
+           ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->
+             ao_idx);
+       spin_unlock(instance->preload_reg_lock);
+
+       //Set single mode, dissable FIFO, dissable external trigger, set output to analog, block interrupt.
+       outl(ME4600_AO_MODE_SINGLE | ME4600_AO_CTRL_BIT_STOP |
+            ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_RESET_IRQ,
+            instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base,
+                  ME4600_AO_MODE_SINGLE | ME4600_AO_CTRL_BIT_STOP |
+                  ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
+                  ME4600_AO_CTRL_BIT_RESET_IRQ);
+
+       //Set output to 0V
+       outl(0x8000, instance->single_reg);
+       PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->single_reg - instance->reg_base, 0x8000);
+
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+       instance->preloaded_count = 0;
+       instance->data_count = 0;
+       instance->single_value = 0x8000;
+       instance->single_value_in_fifo = 0x8000;
+
+       //Set status to signal that device is unconfigured.
+       instance->status = ao_status_none;
+
+       //Signal reset if user is on wait.
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t ctrl;
+       uint32_t sync;
+       unsigned long cpu_flags;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       // Checking parameters
+       if (flags) {
+               PERROR
+                   ("Invalid flag specified. Must be ME_IO_SINGLE_CONFIG_NO_FLAGS.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       switch (trig_type) {
+       case ME_TRIG_TYPE_SW:
+               if (trig_edge != ME_TRIG_EDGE_NONE) {
+                       PERROR
+                           ("Invalid trigger edge. Software trigger has not edge.\n");
+                       return ME_ERRNO_INVALID_TRIG_EDGE;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               switch (trig_edge) {
+               case ME_TRIG_EDGE_ANY:
+               case ME_TRIG_EDGE_RISING:
+               case ME_TRIG_EDGE_FALLING:
+                       break;
+
+               default:
+                       PERROR("Invalid trigger edge.\n");
+                       return ME_ERRNO_INVALID_TRIG_EDGE;
+               }
+               break;
+
+       default:
+               PERROR
+                   ("Invalid trigger type. Trigger must be software or digital.\n");
+               return ME_ERRNO_INVALID_TRIG_TYPE;
+       }
+
+       if ((trig_chan != ME_TRIG_CHAN_DEFAULT)
+           && (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS)) {
+               PERROR("Invalid trigger channel specified.\n");
+               return ME_ERRNO_INVALID_TRIG_CHAN;
+       }
+
+       if (ref != ME_REF_AO_GROUND) {
+               PERROR
+                   ("Invalid reference. Analog outputs have to have got REF_AO_GROUND.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if (single_config != 0) {
+               PERROR
+                   ("Invalid single config specified. Only one range for anlog outputs is available.\n");
+               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+       }
+
+       if (channel != 0) {
+               PERROR
+                   ("Invalid channel number specified. Analog output have only one channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Subdevice running in stream mode!
+       if ((instance->status >= ao_status_stream_run_wait)
+           && (instance->status < ao_status_stream_end)) {
+               PERROR("Subdevice is busy.\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+/// @note For single all calls (config and write) are erasing previous state!
+
+       instance->status = ao_status_none;
+
+       // Correct single mirrors
+       instance->single_value_in_fifo = instance->single_value;
+
+       //Stop device
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+       // Set control register.
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       // Set stop bit. Stop streaming mode.
+       ctrl = inl(instance->ctrl_reg);
+       //Reset all bits.
+       ctrl = ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP;
+
+       if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL) {
+               PINFO("External digital trigger.\n");
+
+               if (trig_edge == ME_TRIG_EDGE_ANY) {
+//                              ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                       instance->ctrl_trg =
+                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
+                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+               } else if (trig_edge == ME_TRIG_EDGE_FALLING) {
+//                              ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+                       instance->ctrl_trg = ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+               } else if (trig_edge == ME_TRIG_EDGE_RISING) {
+                       instance->ctrl_trg = 0x0;
+               }
+       } else if (trig_type == ME_TRIG_TYPE_SW) {
+               PDEBUG("Software trigger\n");
+               instance->ctrl_trg = 0x0;
+       }
+
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       // Set preload/synchronization register.
+       spin_lock(instance->preload_reg_lock);
+       if (trig_type == ME_TRIG_TYPE_SW) {
+               *instance->preload_flags &=
+                   ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx);
+       } else                  //if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL)
+       {
+               *instance->preload_flags |=
+                   ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx;
+       }
+
+       if (trig_chan == ME_TRIG_CHAN_DEFAULT) {
+               *instance->preload_flags &=
+                   ~(ME4600_AO_SYNC_HOLD << instance->ao_idx);
+       } else                  //if (trig_chan == ME_TRIG_CHAN_SYNCHRONOUS)
+       {
+               *instance->preload_flags |=
+                   ME4600_AO_SYNC_HOLD << instance->ao_idx;
+       }
+
+       //Reset hardware register
+       sync = inl(instance->preload_reg);
+       PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, sync);
+       sync &= ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx);
+       sync |= ME4600_AO_SYNC_HOLD << instance->ao_idx;
+
+       //Output configured in default (safe) mode.
+       outl(sync, instance->preload_reg);
+       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, sync);
+       spin_unlock(instance->preload_reg_lock);
+
+       instance->status = ao_status_single_configured;
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       unsigned long j;
+       unsigned long delay = 0;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags & ~ME_IO_SINGLE_NONBLOCKING) {
+               PERROR("Invalid flag specified. %d\n", flags);
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (channel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if ((instance->status >= ao_status_stream_configured)
+           && (instance->status <= ao_status_stream_end)) {
+               PERROR("Subdevice not configured to work in single mode!\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       ME_SUBDEVICE_ENTER;
+       if ((!flags) && (instance->status == ao_status_single_run_wait)) {      //Blocking mode. Wait for trigger.
+               if (time_out) {
+                       delay = (time_out * HZ) / 1000;
+                       if (delay == 0)
+                               delay = 1;
+               }
+
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_single_run_wait),
+                                                (delay) ? delay +
+                                                1 : LONG_MAX);
+
+               if (instance->status == ao_status_none) {
+                       PDEBUG("Single canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - j) >= delay)) {
+
+                       PDEBUG("Timeout reached.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               }
+
+               *value =
+                   (!err) ? instance->single_value_in_fifo : instance->
+                   single_value;
+       } else {                //Non-blocking mode
+               //Read value
+               *value = instance->single_value;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags;
+       unsigned long j;
+       unsigned long delay = 0x0;
+
+       //Registry handling variables.
+       uint32_t sync_mask;
+       uint32_t mode;
+       uint32_t tmp;
+       uint32_t ctrl;
+       uint32_t status;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags &
+           ~(ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS |
+             ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (value & ~ME4600_AO_MAX_DATA) {
+               PERROR("Invalid value provided.\n");
+               return ME_ERRNO_VALUE_OUT_OF_RANGE;
+       }
+
+       if (channel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if ((instance->status == ao_status_none)
+           || (instance->status > ao_status_single_end)) {
+               PERROR("Subdevice not configured to work in single mode!\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+/// @note For single all calls (config and write) are erasing previous state!
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       // Correct single mirrors
+       instance->single_value_in_fifo = instance->single_value;
+
+       //Stop device
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       instance->single_value_in_fifo = value;
+
+       ctrl = inl(instance->ctrl_reg);
+
+       if (!instance->fifo) {  //No FIFO
+               //Set the single mode.
+               ctrl &= ~ME4600_AO_CTRL_MODE_MASK;
+
+               //Write value
+               PDEBUG("Write value\n");
+               outl(value, instance->single_reg);
+               PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->single_reg - instance->reg_base, value);
+       } else {                // mix-mode
+               //Set speed
+               outl(ME4600_AO_MIN_CHAN_TICKS - 1, instance->timer_reg);
+               PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->timer_reg - instance->reg_base,
+                          (int)ME4600_AO_MIN_CHAN_TICKS);
+               instance->hardware_stop_delay = HZ / 10;        //100ms
+
+               status = inl(instance->status_reg);
+
+               //Set the continous mode.
+               ctrl &= ~ME4600_AO_CTRL_MODE_MASK;
+               ctrl |= ME4600_AO_MODE_CONTINUOUS;
+
+               //Prepare FIFO
+               if (!(ctrl & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it.
+                       PINFO("Enableing FIFO.\n");
+                       ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       ctrl |=
+                           ME4600_AO_CTRL_BIT_ENABLE_FIFO |
+                           ME4600_AO_CTRL_BIT_RESET_IRQ;
+               } else {        //Check if FIFO is empty
+                       if (status & ME4600_AO_STATUS_BIT_EF) { //FIFO not empty
+                               PINFO("Reseting FIFO.\n");
+                               ctrl &=
+                                   ~(ME4600_AO_CTRL_BIT_ENABLE_FIFO |
+                                     ME4600_AO_CTRL_BIT_ENABLE_IRQ);
+                               ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+                               outl(ctrl, instance->ctrl_reg);
+                               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->ctrl_reg -
+                                          instance->reg_base, ctrl);
+
+                               ctrl |=
+                                   ME4600_AO_CTRL_BIT_ENABLE_FIFO |
+                                   ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       } else {        //FIFO empty, only interrupt needs to be disabled!
+                               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                               ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       }
+               }
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               //Write output - 1 value to FIFO
+               if (instance->ao_idx & 0x1) {
+                       outl(value <<= 16, instance->fifo_reg);
+                       PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->fifo_reg - instance->reg_base,
+                                  value <<= 16);
+               } else {
+                       outl(value, instance->fifo_reg);
+                       PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->fifo_reg - instance->reg_base,
+                                  value);
+               }
+       }
+
+       mode = *instance->preload_flags >> instance->ao_idx;
+       mode &= (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG);
+
+       PINFO("Triggering mode: 0x%x\n", mode);
+
+       spin_lock(instance->preload_reg_lock);
+       sync_mask = inl(instance->preload_reg);
+       PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, sync_mask);
+       switch (mode) {
+       case 0:         //Individual software
+               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               if (!instance->fifo) {  // No FIFO - In this case resetting 'ME4600_AO_SYNC_HOLD' will trigger output.
+                       if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) {       //Now we can set correct mode. This is exception. It is set to synchronous and triggered later.
+                               sync_mask &=
+                                   ~(ME4600_AO_SYNC_EXT_TRIG << instance->
+                                     ao_idx);
+                               sync_mask |=
+                                   ME4600_AO_SYNC_HOLD << instance->ao_idx;
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               } else {        // FIFO
+                       if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) {       //Now we can set correct mode.
+                               sync_mask &=
+                                   ~((ME4600_AO_SYNC_EXT_TRIG |
+                                      ME4600_AO_SYNC_HOLD) << instance->
+                                     ao_idx);
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               }
+               instance->single_value = value;
+               break;
+
+       case ME4600_AO_SYNC_EXT_TRIG:   //Individual hardware
+               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               if (!instance->fifo) {  // No FIFO - In this case resetting 'ME4600_AO_SYNC_HOLD' will trigger output.
+                       if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) {       //Now we can set correct mode
+                               sync_mask &=
+                                   ~(ME4600_AO_SYNC_EXT_TRIG << instance->
+                                     ao_idx);
+                               sync_mask |=
+                                   ME4600_AO_SYNC_HOLD << instance->ao_idx;
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               } else {        // FIFO
+                       if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) {       //Now we can set correct mode.
+                               sync_mask &=
+                                   ~((ME4600_AO_SYNC_EXT_TRIG |
+                                      ME4600_AO_SYNC_HOLD) << instance->
+                                     ao_idx);
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               }
+               break;
+
+       case ME4600_AO_SYNC_HOLD:       //Synchronous software
+               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+//                                      if((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD)
+               if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG)) {   //Now we can set correct mode
+                       sync_mask |=
+                           ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx;
+//                                              sync_mask &= ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx);
+                       sync_mask |= ME4600_AO_SYNC_HOLD << instance->ao_idx;
+
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               }
+               break;
+
+       case (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG):   //Synchronous hardware
+               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+               if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG)) {   //Now we can set correct mode
+                       sync_mask |=
+                           (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) <<
+                           instance->ao_idx;
+
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               }
+               break;
+       }
+//              spin_unlock(instance->preload_reg_lock);        // Moved down.
+
+       //Activate ISM (remove 'stop' bits)
+       ctrl &=
+           ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
+             ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
+       ctrl |= instance->ctrl_trg;
+       ctrl &= ~(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+/// @note When flag 'ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS' is set than output is triggered. ALWAYS!
+
+       if (!instance->fifo) {  //No FIFO
+               if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       //Fired all software synchronous outputs.
+                       tmp = ~(*instance->preload_flags | 0xFFFF0000);
+                       PINFO
+                           ("Fired all software synchronous outputs. mask:0x%08x\n",
+                            tmp);
+                       tmp |= sync_mask & 0xFFFF0000;
+                       // Add this channel to list
+                       tmp &= ~(ME4600_AO_SYNC_HOLD << instance->ao_idx);
+
+                       //Fire
+                       PINFO("Software trigger.\n");
+                       outl(tmp, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  tmp);
+
+                       //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               } else if (!mode) {     // Add this channel to list
+                       outl(sync_mask &
+                            ~(ME4600_AO_SYNC_HOLD << instance->ao_idx),
+                            instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask & ~(ME4600_AO_SYNC_HOLD <<
+                                                instance->ao_idx));
+
+                       //Fire
+                       PINFO("Software trigger.\n");
+
+                       //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               }
+
+       } else {                // mix-mode - begin
+               if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       //Trigger outputs
+                       //Add channel to start list
+                       outl(sync_mask |
+                            (ME4600_AO_SYNC_HOLD << instance->ao_idx),
+                            instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask | (ME4600_AO_SYNC_HOLD <<
+                                               instance->ao_idx));
+
+                       //Fire
+                       PINFO
+                           ("Fired all software synchronous outputs by software trigger.\n");
+                       outl(0x8000, instance->single_reg);
+                       PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->single_reg - instance->reg_base,
+                                  0x8000);
+
+                       //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               } else if (!mode) {     //Trigger outputs
+/*                     //Remove channel from start list //<== Unnecessary. Removed.
+                       outl(sync_mask & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, tmp);
+*/
+                       //Fire
+                       PINFO("Software trigger.\n");
+                       outl(0x8000, instance->single_reg);
+                       PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->single_reg - instance->reg_base,
+                                  0x8000);
+
+/*                     //Restore save settings //<== Unnecessary. Removed.
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, sync_mask);
+*/
+               }
+       }
+       spin_unlock(instance->preload_reg_lock);
+
+       j = jiffies;
+       instance->status = ao_status_single_run_wait;
+
+       instance->timeout.delay = delay;
+       instance->timeout.start_time = j;
+       instance->ao_control_task_flag = 1;
+       queue_delayed_work(instance->me4600_workqueue,
+                          &instance->ao_control_task, 1);
+
+       if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_single_run_wait),
+                                                (delay) ? delay +
+                                                1 : LONG_MAX);
+
+               if (((!delay) || ((jiffies - j) <= delay))
+                   && (instance->status != ao_status_single_end)) {
+                       PDEBUG("Single canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->ao_control_task_flag = 0;
+                       cancel_delayed_work(&instance->ao_control_task);
+                       ao_stop_immediately(instance);
+                       instance->status = ao_status_none;
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - j) >= delay)) {
+                       if (instance->status == ao_status_single_end) {
+                               PDEBUG("Timeout reached.\n");
+                       } else {
+                               if ((jiffies - j) > delay) {
+                                       PERROR
+                                           ("Timeout reached. Not handled by control task!\n");
+                               } else {
+                                       PERROR
+                                           ("Timeout reached. Signal come but status is strange: %d\n",
+                                            instance->status);
+                               }
+
+                               ao_stop_immediately(instance);
+                       }
+
+                       instance->ao_control_task_flag = 0;
+                       cancel_delayed_work(&instance->ao_control_task);
+                       instance->status = ao_status_single_end;
+                       err = ME_ERRNO_TIMEOUT;
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t ctrl;
+       unsigned long cpu_flags;
+       uint64_t conv_ticks;
+       unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow;
+       unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       conv_ticks =
+           (uint64_t) conv_start_ticks_low +
+           ((uint64_t) conv_start_ticks_high << 32);
+
+       if (flags &
+           ~(ME_IO_STREAM_CONFIG_HARDWARE_ONLY | ME_IO_STREAM_CONFIG_WRAPAROUND
+             | ME_IO_STREAM_CONFIG_BIT_PATTERN)) {
+               PERROR("Invalid flags.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) {
+               if (!flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       PERROR
+                           ("Hardware ME_IO_STREAM_CONFIG_HARDWARE_ONLY has to be with ME_IO_STREAM_CONFIG_WRAPAROUND.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+
+               if ((trigger->iAcqStopTrigType != ME_TRIG_TYPE_NONE)
+                   || (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE)) {
+                       PERROR
+                           ("Hardware wraparound mode must be in infinite mode.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+       }
+
+       if (count != 1) {
+               PERROR("Only 1 entry in config list acceptable.\n");
+               return ME_ERRNO_INVALID_CONFIG_LIST_COUNT;
+       }
+
+       if (config_list[0].iChannel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (config_list[0].iStreamConfig != 0) {
+               PERROR("Only one range available.\n");
+               return ME_ERRNO_INVALID_STREAM_CONFIG;
+       }
+
+       if (config_list[0].iRef != ME_REF_AO_GROUND) {
+               PERROR("Output is referenced to ground.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if ((trigger->iAcqStartTicksLow != 0)
+           || (trigger->iAcqStartTicksHigh != 0)) {
+               PERROR
+                   ("Invalid acquisition start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_ARG;
+       }
+
+       if (config_list[0].iFlags) {
+               PERROR("Invalid config list flag.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       switch (trigger->iAcqStartTrigType) {
+       case ME_TRIG_TYPE_SW:
+               if (trigger->iAcqStartTrigEdge != ME_TRIG_EDGE_NONE) {
+                       PERROR
+                           ("Invalid acquisition start trigger edge specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+               }
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               switch (trigger->iAcqStartTrigEdge) {
+               case ME_TRIG_EDGE_ANY:
+               case ME_TRIG_EDGE_RISING:
+               case ME_TRIG_EDGE_FALLING:
+                       break;
+
+               default:
+                       PERROR
+                           ("Invalid acquisition start trigger edge specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+               }
+               break;
+
+       default:
+               PERROR("Invalid acquisition start trigger type specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;
+       }
+
+       if (trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW) {
+               PERROR("Invalid scan start trigger type specified.\n");
+               return ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+       }
+
+       if (trigger->iConvStartTrigType != ME_TRIG_TYPE_TIMER) {
+               PERROR("Invalid conv start trigger type specified.\n");
+               return ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+       }
+
+       if ((conv_ticks < ME4600_AO_MIN_CHAN_TICKS)
+           || (conv_ticks > ME4600_AO_MAX_CHAN_TICKS)) {
+               PERROR("Invalid conv start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_CONV_START_ARG;
+       }
+
+       if (trigger->iAcqStartTicksLow || trigger->iAcqStartTicksHigh) {
+               PERROR("Invalid acq start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_ARG;
+       }
+
+       if (trigger->iScanStartTicksLow || trigger->iScanStartTicksHigh) {
+               PERROR("Invalid scan start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_SCAN_START_ARG;
+       }
+
+       switch (trigger->iScanStopTrigType) {
+       case ME_TRIG_TYPE_NONE:
+               if (trigger->iScanStopCount != 0) {
+                       PERROR("Invalid scan stop count specified.\n");
+                       return ME_ERRNO_INVALID_SCAN_STOP_ARG;
+               }
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       if (trigger->iScanStopCount <= 0) {
+                               PERROR("Invalid scan stop count specified.\n");
+                               return ME_ERRNO_INVALID_SCAN_STOP_ARG;
+                       }
+               } else {
+                       PERROR("The continous mode has not 'scan' contects.\n");
+                       return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+               }
+               break;
+
+       default:
+               PERROR("Invalid scan stop trigger type specified.\n");
+               return ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE;
+       }
+
+       switch (trigger->iAcqStopTrigType) {
+       case ME_TRIG_TYPE_NONE:
+               if (trigger->iAcqStopCount != 0) {
+                       PERROR("Invalid acq stop count specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_STOP_ARG;
+               }
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
+                       PERROR("Invalid acq stop trigger type specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+               }
+
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       if (trigger->iAcqStopCount <= 0) {
+                               PERROR
+                                   ("The continous mode has not 'scan' contects.\n");
+                               return ME_ERRNO_INVALID_ACQ_STOP_ARG;
+                       }
+               }
+               break;
+
+       default:
+               PERROR("Invalid acq stop trigger type specified.\n");
+               return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+       }
+
+       switch (trigger->iAcqStartTrigChan) {
+       case ME_TRIG_CHAN_DEFAULT:
+       case ME_TRIG_CHAN_SYNCHRONOUS:
+               break;
+
+       default:
+               PERROR("Invalid acq start trigger channel specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if ((flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) && !instance->bitpattern) {
+               PERROR("This subdevice not support output redirection.\n");
+               ME_SUBDEVICE_EXIT;
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+       //Stop device
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       //Check if state machine is stopped.
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       //Reset control register. Block all actions. Disable IRQ. Disable FIFO.
+       ctrl =
+           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP |
+           ME4600_AO_CTRL_BIT_RESET_IRQ;
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+
+       //This is paranoic, but to be sure.
+       instance->preloaded_count = 0;
+       instance->data_count = 0;
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+
+       /* Set mode. */
+       if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {   //Wraparound
+               if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) {        //Hardware wraparound
+                       PINFO("Hardware wraparound.\n");
+                       ctrl |= ME4600_AO_MODE_WRAPAROUND;
+                       instance->mode = ME4600_AO_HW_WRAP_MODE;
+               } else {        //Software wraparound
+                       PINFO("Software wraparound.\n");
+                       ctrl |= ME4600_AO_MODE_CONTINUOUS;
+                       instance->mode = ME4600_AO_SW_WRAP_MODE;
+               }
+       } else {                //Continous
+               PINFO("Continous.\n");
+               ctrl |= ME4600_AO_MODE_CONTINUOUS;
+               instance->mode = ME4600_AO_CONTINOUS;
+       }
+
+       //Set the trigger edge.
+       if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {   //Set the trigger type and edge for external trigger.
+               PINFO("External digital trigger.\n");
+               instance->start_mode = ME4600_AO_EXT_TRIG;
+/*
+                       ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+*/
+               switch (trigger->iAcqStartTrigEdge) {
+               case ME_TRIG_EDGE_RISING:
+                       PINFO("Set the trigger edge: rising.\n");
+                       instance->ctrl_trg = 0x0;
+                       break;
+
+               case ME_TRIG_EDGE_FALLING:
+                       PINFO("Set the trigger edge: falling.\n");
+//                                      ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+                       instance->ctrl_trg = ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+                       break;
+
+               case ME_TRIG_EDGE_ANY:
+                       PINFO("Set the trigger edge: both edges.\n");
+//                                      ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                       instance->ctrl_trg =
+                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
+                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                       break;
+               }
+       } else {
+               PINFO("Internal software trigger.\n");
+               instance->start_mode = 0;
+       }
+
+       //Set the stop mode and value.
+       if (trigger->iAcqStopTrigType == ME_TRIG_TYPE_COUNT) {  //Amount of data
+               instance->stop_mode = ME4600_AO_ACQ_STOP_MODE;
+               instance->stop_count = trigger->iAcqStopCount;
+       } else if (trigger->iScanStopTrigType == ME_TRIG_TYPE_COUNT) {  //Amount of 'scans'
+               instance->stop_mode = ME4600_AO_SCAN_STOP_MODE;
+               instance->stop_count = trigger->iScanStopCount;
+       } else {                //Infinite
+               instance->stop_mode = ME4600_AO_INF_STOP_MODE;
+               instance->stop_count = 0;
+       }
+
+       PINFO("Stop count: %d.\n", instance->stop_count);
+
+       if (trigger->iAcqStartTrigChan == ME_TRIG_CHAN_SYNCHRONOUS) {   //Synchronous start
+               instance->start_mode |= ME4600_AO_SYNC_HOLD;
+               if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {   //Externaly triggered
+                       PINFO("Synchronous start. Externaly trigger active.\n");
+                       instance->start_mode |= ME4600_AO_SYNC_EXT_TRIG;
+               }
+#ifdef MEDEBUG_INFO
+               else {
+                       PINFO
+                           ("Synchronous start. Externaly trigger dissabled.\n");
+               }
+#endif
+
+       }
+       //Set speed
+       outl(conv_ticks - 2, instance->timer_reg);
+       PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%llx\n", instance->reg_base,
+                  instance->timer_reg - instance->reg_base, conv_ticks - 2);
+       instance->hardware_stop_delay = (int)(conv_ticks * HZ) / ME4600_AO_BASE_FREQUENCY;      //<== MUST be with cast!
+
+       //Conect outputs to analog or digital port.
+       if (flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) {
+               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_DO;
+       }
+       // Write the control word
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+
+       //Set status.
+       instance->status = ao_status_stream_configured;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       long j;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!instance->circ_buf.buf) {
+               PERROR("Circular buffer not exists.\n");
+               return ME_ERRNO_INTERNAL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (me_circ_buf_space(&instance->circ_buf)) {   //The buffer is NOT full.
+               *count = me_circ_buf_space(&instance->circ_buf);
+       } else {                //The buffer is full.
+               if (time_out) {
+                       t = (time_out * HZ) / 1000;
+
+                       if (t == 0)
+                               t = 1;
+               } else {        //Max time.
+                       t = LONG_MAX;
+               }
+
+               *count = 0;
+
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Interrupts are when FIFO HF is signaled.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((me_circ_buf_space
+                                                  (&instance->circ_buf))
+                                                 || !(inl(instance->status_reg)
+                                                      &
+                                                      ME4600_AO_STATUS_BIT_FSM)),
+                                                t);
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
+                       PERROR("AO subdevice is not running.\n");
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+               } else if (signal_pending(current)) {
+                       PERROR("Wait on values interrupted from signal.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               } else if ((jiffies - j) >= t) {
+                       PERROR("Wait on values timed out.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               } else {        //Uff... all is good. Inform user about empty space.
+                       *count = me_circ_buf_space(&instance->circ_buf);
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags = 0;
+       uint32_t status;
+       uint32_t ctrl;
+       uint32_t synch;
+       int count = 0;
+       int circ_buffer_count;
+
+       unsigned long ref;
+       unsigned long delay = 0;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags & ~ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
+               PERROR("Invalid flags.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if ((start_mode != ME_START_MODE_BLOCKING)
+           && (start_mode != ME_START_MODE_NONBLOCKING)) {
+               PERROR("Invalid start mode specified.\n");
+               return ME_ERRNO_INVALID_START_MODE;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       switch (instance->status) {     //Checking actual mode.
+       case ao_status_stream_configured:
+       case ao_status_stream_end:
+               //Correct modes!
+               break;
+
+               //The device is in wrong mode.
+       case ao_status_none:
+       case ao_status_single_configured:
+       case ao_status_single_run_wait:
+       case ao_status_single_run:
+       case ao_status_single_end_wait:
+               PERROR
+                   ("Subdevice must be preinitialize correctly for streaming.\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+
+       case ao_status_stream_fifo_error:
+       case ao_status_stream_buffer_error:
+       case ao_status_stream_error:
+               PDEBUG("Before restart broke stream 'STOP' must be caled.\n");
+               return ME_STATUS_ERROR;
+
+       case ao_status_stream_run_wait:
+       case ao_status_stream_run:
+       case ao_status_stream_end_wait:
+               PDEBUG("Stream is already working.\n");
+               return ME_ERRNO_SUBDEVICE_BUSY;
+
+       default:
+               instance->status = ao_status_stream_error;
+               PERROR_CRITICAL("Status is in wrong state!\n");
+               return ME_ERRNO_INTERNAL;
+
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
+               instance->circ_buf.tail += instance->preloaded_count;
+               instance->circ_buf.tail &= instance->circ_buf.mask;
+       }
+       circ_buffer_count = me_circ_buf_values(&instance->circ_buf);
+
+       if (!circ_buffer_count && !instance->preloaded_count) { //No values in buffer
+               ME_SUBDEVICE_EXIT;
+               PERROR("No values in buffer!\n");
+               return ME_ERRNO_LACK_OF_RESOURCES;
+       }
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       //Stop device
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+       //Set values for single_read()
+       instance->single_value = ME4600_AO_MAX_DATA + 1;
+       instance->single_value_in_fifo = ME4600_AO_MAX_DATA + 1;
+
+       //Setting stop points
+       if (instance->stop_mode == ME4600_AO_SCAN_STOP_MODE) {
+               instance->stop_data_count =
+                   instance->stop_count * circ_buffer_count;
+       } else {
+               instance->stop_data_count = instance->stop_count;
+       }
+
+       if ((instance->stop_data_count != 0)
+           && (instance->stop_data_count < circ_buffer_count)) {
+               PERROR("More data in buffer than previously set limit!\n");
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       ctrl = inl(instance->ctrl_reg);
+       //Check FIFO
+       if (!(ctrl & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it. <= This should be done by user call with ME_WRITE_MODE_PRELOAD
+               PINFO("Enableing FIFO.\n");
+               ctrl |=
+                   ME4600_AO_CTRL_BIT_ENABLE_FIFO |
+                   ME4600_AO_CTRL_BIT_RESET_IRQ;
+
+               instance->preloaded_count = 0;
+               instance->data_count = 0;
+       } else {                //Block IRQ
+               ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+       }
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base,
+                  ctrl | ME4600_AO_CTRL_BIT_RESET_IRQ);
+
+       //Fill FIFO <= Generaly this should be done by user pre-load call but this is second place to do it.
+       status = inl(instance->status_reg);
+       if (!(status & ME4600_AO_STATUS_BIT_EF)) {      //FIFO empty
+               if (instance->stop_data_count == 0) {
+                       count = ME4600_AO_FIFO_COUNT;
+               } else {
+                       count =
+                           (ME4600_AO_FIFO_COUNT <
+                            instance->
+                            stop_data_count) ? ME4600_AO_FIFO_COUNT :
+                           instance->stop_data_count;
+               }
+
+               //Copy data
+               count =
+                   ao_write_data(instance, count, instance->preloaded_count);
+
+               if (count < 0) {        //This should never happend!
+                       PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+       }
+       //Set pre-load features.
+       spin_lock(instance->preload_reg_lock);
+       synch = inl(instance->preload_reg);
+       synch &=
+           ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->
+             ao_idx);
+       synch |=
+           (instance->start_mode & ~ME4600_AO_EXT_TRIG) << instance->ao_idx;
+       outl(synch, instance->preload_reg);
+       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, synch);
+       spin_unlock(instance->preload_reg_lock);
+
+       //Default count is '0'
+       if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
+               instance->preloaded_count = 0;
+               instance->circ_buf.tail += count;
+               instance->circ_buf.tail &= instance->circ_buf.mask;
+       } else {                //Wraparound
+               instance->preloaded_count += count;
+               instance->data_count += count;
+
+               //Special case: Infinite wraparound with less than FIFO datas always should runs in hardware mode.
+               if ((instance->stop_mode == ME4600_AO_INF_STOP_MODE)
+                   && (circ_buffer_count <= ME4600_AO_FIFO_COUNT)) {   //Change to hardware wraparound
+                       PDEBUG
+                           ("Changeing mode from software wraparound to hardware wraparound.\n");
+                       //Copy all data
+                       count =
+                           ao_write_data(instance, circ_buffer_count,
+                                         instance->preloaded_count);
+                       ctrl &= ~ME4600_AO_CTRL_MODE_MASK;
+                       ctrl |= ME4600_AO_MODE_WRAPAROUND;
+               }
+
+               if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) {     //Reset position indicator.
+                       instance->preloaded_count = 0;
+               } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) {       //This should never happend!
+                       PERROR_CRITICAL
+                           ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n");
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+       }
+
+       //Set status to 'wait for start'
+       instance->status = ao_status_stream_run_wait;
+
+       status = inl(instance->status_reg);
+       //Start state machine and interrupts
+       ctrl &= ~(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+       if (instance->start_mode == ME4600_AO_EXT_TRIG) {       // External trigger.
+               PINFO("External trigger.\n");
+               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+       }
+       if (!(status & ME4600_AO_STATUS_BIT_HF)) {      //More than half!
+               if ((ctrl & ME4600_AO_CTRL_MODE_MASK) == ME4600_AO_MODE_CONTINUOUS) {   //Enable IRQ only when hardware_continous is set and FIFO is more than half
+                       ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+               }
+       }
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       //Trigger output
+       if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       //Trigger outputs
+               spin_lock(instance->preload_reg_lock);
+               synch = inl(instance->preload_reg);
+               //Add channel to start list
+               outl(synch | (ME4600_AO_SYNC_HOLD << instance->ao_idx),
+                    instance->preload_reg);
+               PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->preload_reg - instance->reg_base,
+                          synch | (ME4600_AO_SYNC_HOLD << instance->ao_idx));
+
+               //Fire
+               PINFO
+                   ("Fired all software synchronous outputs by software trigger.\n");
+               outl(0x8000, instance->single_reg);
+               PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->single_reg - instance->reg_base, 0x8000);
+
+               //Restore save settings
+               outl(synch, instance->preload_reg);
+               PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->preload_reg - instance->reg_base, synch);
+               spin_unlock(instance->preload_reg_lock);
+       } else if (!instance->start_mode) {     //Trigger outputs
+/*
+               //Remove channel from start list.       // <== Unnecessary. Removed.
+               spin_lock(instance->preload_reg_lock);
+                       synch = inl(instance->preload_reg);
+                       outl(synch & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx));
+*/
+               //Fire
+               PINFO("Software trigger.\n");
+               outl(0x8000, instance->single_reg);
+               PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->single_reg - instance->reg_base, 0x8000);
+
+/*
+                       //Restore save settings.        // <== Unnecessary. Removed.
+                       outl(synch, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch);
+               spin_unlock(instance->preload_reg_lock);
+*/
+       }
+       // Set control task's timeout
+       ref = jiffies;
+       instance->timeout.delay = delay;
+       instance->timeout.start_time = ref;
+
+       if (status & ME4600_AO_STATUS_BIT_HF) { //Less than half but not empty!
+               PINFO("Less than half.\n");
+               if (instance->stop_data_count != 0) {
+                       count = ME4600_AO_FIFO_COUNT / 2;
+               } else {
+                       count =
+                           ((ME4600_AO_FIFO_COUNT / 2) <
+                            instance->stop_data_count) ? ME4600_AO_FIFO_COUNT /
+                           2 : instance->stop_data_count;
+               }
+
+               //Copy data
+               count =
+                   ao_write_data(instance, count, instance->preloaded_count);
+
+               if (count < 0) {        //This should never happend!
+                       PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+
+               if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
+                       instance->circ_buf.tail += count;
+                       instance->circ_buf.tail &= instance->circ_buf.mask;
+               } else {        //Wraparound
+                       instance->data_count += count;
+                       instance->preloaded_count += count;
+
+                       if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) {     //Reset position indicator.
+                               instance->preloaded_count = 0;
+                       } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) {       //This should never happend!
+                               PERROR_CRITICAL
+                                   ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n");
+                               ME_SUBDEVICE_EXIT;
+                               return ME_ERRNO_INTERNAL;
+                       }
+               }
+
+               status = inl(instance->status_reg);
+               if (!(status & ME4600_AO_STATUS_BIT_HF)) {      //More than half!
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               }
+       }
+       //Special case: Limited wraparound with less than HALF FIFO datas need work around to generate first interrupt.
+       if ((instance->stop_mode != ME4600_AO_INF_STOP_MODE)
+           && (instance->mode == ME4600_AO_SW_WRAP_MODE)
+           && (circ_buffer_count <= (ME4600_AO_FIFO_COUNT / 2))) {     //Put more data to FIFO
+               PINFO("Limited wraparound with less than HALF FIFO datas.\n");
+               if (instance->preloaded_count) {        //This should never happend!
+                       PERROR_CRITICAL
+                           ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n");
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+
+               while (instance->stop_data_count > instance->data_count) {      //Maximum data not set jet.
+                       //Copy to buffer
+                       if (circ_buffer_count != ao_write_data(instance, circ_buffer_count, 0)) {       //This should never happend!
+                               PERROR_CRITICAL
+                                   ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n");
+                               ME_SUBDEVICE_EXIT;
+                               return ME_ERRNO_INTERNAL;
+                       }
+                       instance->data_count += circ_buffer_count;
+
+                       if (!((status = inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_HF)) {        //FIFO is more than half. Enable IRQ and end copy.
+                               spin_lock_irqsave(&instance->subdevice_lock,
+                                                 cpu_flags);
+                               ctrl = inl(instance->ctrl_reg);
+                               ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+                               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                               outl(ctrl, instance->ctrl_reg);
+                               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->ctrl_reg -
+                                          instance->reg_base, ctrl);
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               break;
+                       }
+               }
+       }
+       // Schedule control task.
+       instance->ao_control_task_flag = 1;
+       queue_delayed_work(instance->me4600_workqueue,
+                          &instance->ao_control_task, 1);
+
+       if (start_mode == ME_START_MODE_BLOCKING) {     //Wait for start.
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_stream_run_wait),
+                                                (delay) ? delay +
+                                                1 : LONG_MAX);
+
+               if ((instance->status != ao_status_stream_run)
+                   && (instance->status != ao_status_stream_end)) {
+                       PDEBUG("Starting stream canceled. %d\n",
+                              instance->status);
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               } else if ((delay) && ((jiffies - ref) >= delay)) {
+                       if (instance->status != ao_status_stream_run) {
+                               if (instance->status == ao_status_stream_end) {
+                                       PDEBUG("Timeout reached.\n");
+                               } else {
+                                       if ((jiffies - ref) > delay) {
+                                               PERROR
+                                                   ("Timeout reached. Not handled by control task!\n");
+                                       } else {
+                                               PERROR
+                                                   ("Timeout reached. Signal come but status is strange: %d\n",
+                                                    instance->status);
+                                       }
+                                       ao_stop_immediately(instance);
+                               }
+
+                               instance->ao_control_task_flag = 0;
+                               cancel_delayed_work(&instance->ao_control_task);
+                               instance->status = ao_status_stream_end;
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+       return err;
+}
+
+static int me4600_ao_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((wait != ME_WAIT_NONE) && (wait != ME_WAIT_IDLE)) {
+               PERROR("Invalid wait argument specified.\n");
+               *status = ME_STATUS_INVALID;
+               return ME_ERRNO_INVALID_WAIT;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       switch (instance->status) {
+       case ao_status_single_configured:
+       case ao_status_single_end:
+       case ao_status_stream_configured:
+       case ao_status_stream_end:
+       case ao_status_stream_fifo_error:
+       case ao_status_stream_buffer_error:
+       case ao_status_stream_error:
+               *status = ME_STATUS_IDLE;
+               break;
+
+       case ao_status_single_run_wait:
+       case ao_status_single_run:
+       case ao_status_single_end_wait:
+       case ao_status_stream_run_wait:
+       case ao_status_stream_run:
+       case ao_status_stream_end_wait:
+               *status = ME_STATUS_BUSY;
+               break;
+
+       case ao_status_none:
+       default:
+               *status =
+                   (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ?
+                   ME_STATUS_BUSY : ME_STATUS_IDLE;
+               break;
+       }
+
+       if ((wait == ME_WAIT_IDLE) && (*status == ME_STATUS_BUSY)) {
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((instance->status !=
+                                                  ao_status_single_run_wait)
+                                                 && (instance->status !=
+                                                     ao_status_single_run)
+                                                 && (instance->status !=
+                                                     ao_status_single_end_wait)
+                                                 && (instance->status !=
+                                                     ao_status_stream_run_wait)
+                                                 && (instance->status !=
+                                                     ao_status_stream_run)
+                                                 && (instance->status !=
+                                                     ao_status_stream_end_wait)),
+                                                LONG_MAX);
+
+               if (instance->status != ao_status_stream_end) {
+                       PDEBUG("Wait for IDLE canceled. %d\n",
+                              instance->status);
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait for IDLE interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               *status = ME_STATUS_IDLE;
+       }
+
+       *values = me_circ_buf_space(&instance->circ_buf);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags)
+{                              // Stop work and empty buffer and FIFO
+       int err = ME_ERRNO_SUCCESS;
+       me4600_ao_subdevice_t *instance;
+       unsigned long cpu_flags;
+       volatile uint32_t ctrl;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags & ~ME_IO_STREAM_STOP_PRESERVE_BUFFERS) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((stop_mode != ME_STOP_MODE_IMMEDIATE)
+           && (stop_mode != ME_STOP_MODE_LAST_VALUE)) {
+               PERROR("Invalid stop mode specified.\n");
+               return ME_ERRNO_INVALID_STOP_MODE;
+       }
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (instance->status < ao_status_stream_configured) {
+               //There is nothing to stop!
+               PERROR("Subdevice not in streaming mode. %d\n",
+                      instance->status);
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Mark as stopping. => Software stop.
+       instance->status = ao_status_stream_end_wait;
+
+       if (stop_mode == ME_STOP_MODE_IMMEDIATE) {      //Stopped now!
+               err = ao_stop_immediately(instance);
+       } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) {
+               ctrl = inl(instance->ctrl_reg) & ME4600_AO_CTRL_MODE_MASK;
+               if (ctrl == ME4600_AO_MODE_WRAPAROUND) {        //Hardware wraparound => Hardware stop.
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           ME4600_AO_CTRL_BIT_STOP |
+                           ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               }
+               //Only runing process will interrupt this call. Events are signaled when status change.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_stream_end_wait),
+                                                LONG_MAX);
+
+               if (instance->status != ao_status_stream_end) {
+                       PDEBUG("Stopping stream canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Stopping stream interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       ctrl = inl(instance->ctrl_reg);
+       ctrl |=
+           ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
+           ME4600_AO_CTRL_BIT_RESET_IRQ;
+       ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+       if (!flags) {           //Reset FIFO
+               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO;
+       }
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       if (!flags) {           //Reset software buffer
+               instance->circ_buf.head = 0;
+               instance->circ_buf.tail = 0;
+               instance->preloaded_count = 0;
+               instance->data_count = 0;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int write_mode,
+                                    int *values, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me4600_ao_subdevice_t *instance;
+       unsigned long cpu_flags = 0;
+       uint32_t reg_copy;
+
+       int copied_from_user = 0;
+       int left_to_copy_from_user = *count;
+
+       int copied_values;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       //Checking arguments
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (*count <= 0) {
+               PERROR("Invalid count of values specified.\n");
+               return ME_ERRNO_INVALID_VALUE_COUNT;
+       }
+
+       if (values == NULL) {
+               PERROR("Invalid address of values specified.\n");
+               return ME_ERRNO_INVALID_POINTER;
+       }
+
+       if ((instance->status == ao_status_none) || (instance->status == ao_status_single_configured)) {        //The device is in single mode.
+               PERROR
+                   ("Subdevice must be preinitialize correctly for streaming.\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+/// @note If no 'pre-load' is used. stream_start() will move data to FIFO.
+       switch (write_mode) {
+       case ME_WRITE_MODE_PRELOAD:
+
+               //Device must be stopped.
+               if ((instance->status != ao_status_stream_configured)
+                   && (instance->status != ao_status_stream_end)) {
+                       PERROR
+                           ("Subdevice mustn't be runing when 'pre-load' mode is used.\n");
+                       return ME_ERRNO_PREVIOUS_CONFIG;
+               }
+               break;
+       case ME_WRITE_MODE_NONBLOCKING:
+       case ME_WRITE_MODE_BLOCKING:
+               /// @note In blocking mode: When device is not runing and there is not enought space call will blocked up!
+               /// @note Some other thread must empty buffer by starting engine.
+               break;
+
+       default:
+               PERROR("Invalid write mode specified.\n");
+               return ME_ERRNO_INVALID_WRITE_MODE;
+       }
+
+       if (instance->mode & ME4600_AO_WRAP_MODE) {     //Wraparound mode. Device must be stopped.
+               if ((instance->status != ao_status_stream_configured)
+                   && (instance->status != ao_status_stream_end)) {
+                       PERROR
+                           ("Subdevice mustn't be runing when 'pre-load' mode is used.\n");
+                       return ME_ERRNO_INVALID_WRITE_MODE;
+               }
+       }
+
+       if ((instance->mode == ME4600_AO_HW_WRAP_MODE) && (write_mode != ME_WRITE_MODE_PRELOAD)) {      // hardware wrap_around mode.
+               //This is transparent for user.
+               PDEBUG("Changing write_mode to ME_WRITE_MODE_PRELOAD.\n");
+               write_mode = ME_WRITE_MODE_PRELOAD;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (write_mode == ME_WRITE_MODE_PRELOAD) {      //Init enviroment - preload
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               reg_copy = inl(instance->ctrl_reg);
+               //Check FIFO
+               if (!(reg_copy & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) {     //FIFO not active. Enable it.
+                       reg_copy |= ME4600_AO_CTRL_BIT_ENABLE_FIFO;
+                       outl(reg_copy, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  reg_copy);
+                       instance->preloaded_count = 0;
+               }
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       }
+
+       while (1) {
+               //Copy to buffer. This step is common for all modes.
+               copied_from_user =
+                   ao_get_data_from_user(instance, left_to_copy_from_user,
+                                         values + (*count -
+                                                   left_to_copy_from_user));
+               left_to_copy_from_user -= copied_from_user;
+
+               reg_copy = inl(instance->status_reg);
+               if ((instance->status == ao_status_stream_run) && !(reg_copy & ME4600_AO_STATUS_BIT_FSM)) {     //BROKEN PIPE! The state machine is stoped but logical status show that should be working.
+                       PERROR("Broken pipe in write.\n");
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+                       break;
+               }
+
+               if ((instance->status == ao_status_stream_run) && (instance->mode == ME4600_AO_CONTINOUS) && (reg_copy & ME4600_AO_STATUS_BIT_HF)) {    //Continous mode runing and data are below half!
+
+                       // Block interrupts.
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       reg_copy = inl(instance->ctrl_reg);
+                       //reg_copy &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       reg_copy |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       outl(reg_copy, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  reg_copy);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       //Fast copy
+                       copied_values =
+                           ao_write_data(instance, ME4600_AO_FIFO_COUNT / 2,
+                                         0);
+                       if (copied_values > 0) {
+                               instance->circ_buf.tail += copied_values;
+                               instance->circ_buf.tail &=
+                                   instance->circ_buf.mask;
+                               continue;
+                       }
+                       // Activate interrupts.
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       reg_copy = inl(instance->ctrl_reg);
+                       //reg_copy |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       reg_copy &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       outl(reg_copy, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  reg_copy);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (copied_values == 0) {       //This was checked and never should happend!
+                               PERROR_CRITICAL("COPING FINISH WITH 0!\n");
+                       }
+
+                       if (copied_values < 0) {        //This was checked and never should happend!
+                               PERROR_CRITICAL
+                                   ("COPING FINISH WITH AN ERROR!\n");
+                               instance->status = ao_status_stream_fifo_error;
+                               err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+                               break;
+                       }
+               }
+
+               if (!left_to_copy_from_user) {  //All datas were copied.
+                       break;
+               } else {        //Not all datas were copied.
+                       if (instance->mode & ME4600_AO_WRAP_MODE) {     //Error too much datas! Wraparound is limited in size!
+                               PERROR
+                                   ("Too much data for wraparound mode!  Exceeded size of %ld.\n",
+                                    ME4600_AO_CIRC_BUF_COUNT - 1);
+                               err = ME_ERRNO_RING_BUFFER_OVERFLOW;
+                               break;
+                       }
+
+                       if (write_mode != ME_WRITE_MODE_BLOCKING) {     //Non blocking calls
+                               break;
+                       }
+
+                       wait_event_interruptible(instance->wait_queue,
+                                                me_circ_buf_space(&instance->
+                                                                  circ_buf));
+
+                       if (signal_pending(current)) {
+                               PERROR("Writing interrupted by signal.\n");
+                               instance->status = ao_status_none;
+                               ao_stop_immediately(instance);
+                               err = ME_ERRNO_SIGNAL;
+                               break;
+                       }
+
+                       if (instance->status == ao_status_none) {       //Reset
+                               PERROR("Writing interrupted by reset.\n");
+                               err = ME_ERRNO_CANCELLED;
+                               break;
+                       }
+               }
+       }
+
+       if (write_mode == ME_WRITE_MODE_PRELOAD) {      //Copy data to FIFO - preload
+               copied_values =
+                   ao_write_data_pooling(instance, ME4600_AO_FIFO_COUNT,
+                                         instance->preloaded_count);
+               instance->preloaded_count += copied_values;
+               instance->data_count += copied_values;
+
+               if ((instance->mode == ME4600_AO_HW_WRAP_MODE)
+                   && (me_circ_buf_values(&instance->circ_buf) >
+                       ME4600_AO_FIFO_COUNT)) {
+                       PERROR
+                           ("Too much data for hardware wraparound mode! Exceeded size of %d.\n",
+                            ME4600_AO_FIFO_COUNT);
+                       err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+               }
+       }
+
+       *count = *count - left_to_copy_from_user;
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+static irqreturn_t me4600_ao_isr(int irq, void *dev_id
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+                                , struct pt_regs *regs
+#endif
+    )
+{
+       me4600_ao_subdevice_t *instance = dev_id;
+       uint32_t irq_status;
+       uint32_t ctrl;
+       uint32_t status;
+       int count = 0;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       irq_status = inl(instance->irq_status_reg);
+       if (!(irq_status & (ME4600_IRQ_STATUS_BIT_AO_HF << instance->ao_idx))) {
+               PINFO("%ld Shared interrupt. %s(): ID=%d: status_reg=0x%04X\n",
+                     jiffies, __FUNCTION__, instance->ao_idx, irq_status);
+               return IRQ_NONE;
+       }
+
+       if (!instance->circ_buf.buf) {
+               instance->status = ao_status_stream_error;
+               PERROR_CRITICAL("CIRCULAR BUFFER NOT EXISTS!\n");
+               //Block interrupts. Stop machine.
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+               ctrl |=
+                   ME4600_AO_CTRL_BIT_RESET_IRQ |
+                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               //Inform user
+               wake_up_interruptible_all(&instance->wait_queue);
+               return IRQ_HANDLED;
+       }
+
+       status = inl(instance->status_reg);
+       if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     //Too late. Not working! END? BROKEN PIPE?
+               PDEBUG("Interrupt come but ISM is not working!\n");
+               //Block interrupts. Stop machine.
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+               ctrl |=
+                   ME4600_AO_CTRL_BIT_RESET_IRQ | ME4600_AO_CTRL_BIT_STOP |
+                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               return IRQ_HANDLED;
+       }
+       //General procedure. Process more datas.
+
+#ifdef MEDEBUG_DEBUG
+       if (!me_circ_buf_values(&instance->circ_buf)) { //Buffer is empty!
+               PDEBUG("Circular buffer empty!\n");
+       }
+#endif
+
+       //Check FIFO
+       if (status & ME4600_AO_STATUS_BIT_HF) { //OK less than half
+
+               //Block interrupts
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+               ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               do {
+                       //Calculate how many should be copied.
+                       count =
+                           (instance->stop_data_count) ? instance->
+                           stop_data_count -
+                           instance->data_count : ME4600_AO_FIFO_COUNT / 2;
+                       if (ME4600_AO_FIFO_COUNT / 2 < count) {
+                               count = ME4600_AO_FIFO_COUNT / 2;
+                       }
+                       //Copy data
+                       if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
+                               count = ao_write_data(instance, count, 0);
+                               if (count > 0) {
+                                       instance->circ_buf.tail += count;
+                                       instance->circ_buf.tail &=
+                                           instance->circ_buf.mask;
+                                       instance->data_count += count;
+
+                                       if ((instance->status == ao_status_stream_end_wait) && !me_circ_buf_values(&instance->circ_buf)) {      //Stoping. Whole buffer was copied.
+                                               break;
+                                       }
+                               }
+                       } else if ((instance->mode == ME4600_AO_SW_WRAP_MODE) && ((ctrl & ME4600_AO_CTRL_MODE_MASK) == ME4600_AO_MODE_CONTINUOUS)) {    //Wraparound (software)
+                               if (instance->status == ao_status_stream_end_wait) {    //We stoping => Copy to the end of the buffer.
+                                       count =
+                                           ao_write_data(instance, count, 0);
+                               } else {        //Copy in wraparound mode.
+                                       count =
+                                           ao_write_data_wraparound(instance,
+                                                                    count,
+                                                                    instance->
+                                                                    preloaded_count);
+                               }
+
+                               if (count > 0) {
+                                       instance->data_count += count;
+                                       instance->preloaded_count += count;
+                                       instance->preloaded_count %=
+                                           me_circ_buf_values(&instance->
+                                                              circ_buf);
+
+                                       if ((instance->status == ao_status_stream_end_wait) && !instance->preloaded_count) {    //Stoping. Whole buffer was copied.
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if ((count <= 0) || (instance->stop_data_count && (instance->stop_data_count <= instance->data_count))) {       //End of work.
+                               break;
+                       }
+               }               //Repeat if still is under half fifo
+               while ((status =
+                       inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_HF);
+
+               //Unblock interrupts
+               ctrl = inl(instance->ctrl_reg);
+               if (count >= 0) {       //Copy was successful.
+                       if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) { //Finishing work. No more interrupts.
+                               PDEBUG("Finishing work. Interrupt disabled.\n");
+                               instance->status = ao_status_stream_end_wait;
+                       } else if (count > 0) { //Normal work. Enable interrupt.
+                               PDEBUG("Normal work. Enable interrupt.\n");
+                               ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+                               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       } else {        //Normal work but there are no more data in buffer. Interrupt active but blocked. stream_write() will unblock it.
+                               PDEBUG
+                                   ("No data in software buffer. Interrupt blocked.\n");
+                               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       }
+               } else {        //Error during copy.
+                       instance->status = ao_status_stream_fifo_error;
+               }
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+       } else {                //?? more than half
+               PDEBUG
+                   ("Interrupt come but FIFO more than half full! Reset interrupt.\n");
+               //Reset pending interrupt
+               ctrl = inl(instance->ctrl_reg);
+               ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+       }
+
+       PINFO("ISR: Buffer count: %d.(T:%d H:%d)\n",
+             me_circ_buf_values(&instance->circ_buf), instance->circ_buf.tail,
+             instance->circ_buf.head);
+       PINFO("ISR: Stop count: %d.\n", instance->stop_count);
+       PINFO("ISR: Stop data count: %d.\n", instance->stop_data_count);
+       PINFO("ISR: Data count: %d.\n", instance->data_count);
+
+       //Inform user
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+static void me4600_ao_destructor(struct me_subdevice *subdevice)
+{
+       me4600_ao_subdevice_t *instance;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       instance->ao_control_task_flag = 0;
+
+       // Reset subdevice to asure clean exit.
+       me4600_ao_io_reset_subdevice(subdevice, NULL,
+                                    ME_IO_RESET_SUBDEVICE_NO_FLAGS);
+
+       // Remove any tasks from work queue. This is paranoic because it was done allready in reset().
+       if (!cancel_delayed_work(&instance->ao_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue.
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(2);
+       }
+
+       if (instance->fifo) {
+               if (instance->irq) {
+                       free_irq(instance->irq, instance);
+                       instance->irq = 0;
+               }
+
+               if (instance->circ_buf.buf) {
+                       free_pages((unsigned long)instance->circ_buf.buf,
+                                  ME4600_AO_CIRC_BUF_SIZE_ORDER);
+               }
+               instance->circ_buf.buf = NULL;
+       }
+
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
+                                            spinlock_t * preload_reg_lock,
+                                            uint32_t * preload_flags,
+                                            int ao_idx,
+                                            int fifo,
+                                            int irq,
+                                            struct workqueue_struct *me4600_wq)
+{
+       me4600_ao_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed. idx=%d\n", ao_idx);
+
+       // Allocate memory for subdevice instance.
+       subdevice = kmalloc(sizeof(me4600_ao_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_ao_subdevice_t));
+
+       // Initialize subdevice base class.
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->preload_reg_lock = preload_reg_lock;
+       subdevice->preload_flags = preload_flags;
+
+       // Store analog output index.
+       subdevice->ao_idx = ao_idx;
+
+       // Store if analog output has fifo.
+       subdevice->fifo = (ao_idx < fifo) ? 1 : 0;
+
+       if (subdevice->fifo) {  // Allocate and initialize circular buffer.
+               subdevice->circ_buf.mask = ME4600_AO_CIRC_BUF_COUNT - 1;
+
+               subdevice->circ_buf.buf =
+                   (void *)__get_free_pages(GFP_KERNEL,
+                                            ME4600_AO_CIRC_BUF_SIZE_ORDER);
+               PDEBUG("circ_buf = %p size=%ld\n", subdevice->circ_buf.buf,
+                      ME4600_AO_CIRC_BUF_SIZE);
+
+               if (!subdevice->circ_buf.buf) {
+                       PERROR
+                           ("Cannot initialize subdevice base class instance.\n");
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+               memset(subdevice->circ_buf.buf, 0, ME4600_AO_CIRC_BUF_SIZE);
+       } else {                // No FIFO.
+               subdevice->circ_buf.mask = 0;
+               subdevice->circ_buf.buf = NULL;
+       }
+
+       subdevice->circ_buf.head = 0;
+       subdevice->circ_buf.tail = 0;
+
+       subdevice->status = ao_status_none;
+       subdevice->ao_control_task_flag = 0;
+       subdevice->timeout.delay = 0;
+       subdevice->timeout.start_time = jiffies;
+
+       // Initialize wait queue.
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       // Initialize single value to 0V.
+       subdevice->single_value = 0x8000;
+       subdevice->single_value_in_fifo = 0x8000;
+
+       // Register interrupt service routine.
+       if (subdevice->fifo) {
+               subdevice->irq = irq;
+               if (request_irq(subdevice->irq, me4600_ao_isr,
+#ifdef IRQF_DISABLED
+                               IRQF_DISABLED | IRQF_SHARED,
+#else
+                               SA_INTERRUPT | SA_SHIRQ,
+#endif
+                               ME4600_NAME, subdevice)) {
+                       PERROR("Cannot get interrupt line.\n");
+                       PDEBUG("free circ_buf = %p size=%d",
+                              subdevice->circ_buf.buf,
+                              PAGE_SHIFT << ME4600_AO_CIRC_BUF_SIZE_ORDER);
+                       free_pages((unsigned long)subdevice->circ_buf.buf,
+                                  ME4600_AO_CIRC_BUF_SIZE_ORDER);
+                       me_subdevice_deinit((me_subdevice_t *) subdevice);
+                       kfree(subdevice);
+                       return NULL;
+               }
+               PINFO("Registered irq=%d.\n", subdevice->irq);
+       } else {
+               subdevice->irq = 0;
+       }
+
+       // Initialize registers.
+       subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
+       subdevice->preload_reg = reg_base + ME4600_AO_SYNC_REG;
+       if (ao_idx == 0) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_00_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_00_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_00_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_00_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_00_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bitpattern = 0;
+       } else if (ao_idx == 1) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_01_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_01_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_01_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_01_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_01_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bitpattern = 0;
+       } else if (ao_idx == 2) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_02_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_02_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_02_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_02_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_02_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bitpattern = 0;
+       } else if (ao_idx == 3) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_03_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_03_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_03_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_03_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_03_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bitpattern = 1;
+       } else {
+               PERROR_CRITICAL("WRONG SUBDEVICE idx=%d!", ao_idx);
+               me_subdevice_deinit((me_subdevice_t *) subdevice);
+               if (subdevice->fifo) {
+                       free_pages((unsigned long)subdevice->circ_buf.buf,
+                                  ME4600_AO_CIRC_BUF_SIZE_ORDER);
+               }
+               subdevice->circ_buf.buf = NULL;
+               kfree(subdevice);
+               return NULL;
+       }
+
+       // Override base class methods.
+       subdevice->base.me_subdevice_destructor = me4600_ao_destructor;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_ao_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me4600_ao_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me4600_ao_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me4600_ao_io_single_write;
+       subdevice->base.me_subdevice_io_stream_config =
+           me4600_ao_io_stream_config;
+       subdevice->base.me_subdevice_io_stream_new_values =
+           me4600_ao_io_stream_new_values;
+       subdevice->base.me_subdevice_io_stream_write =
+           me4600_ao_io_stream_write;
+       subdevice->base.me_subdevice_io_stream_start =
+           me4600_ao_io_stream_start;
+       subdevice->base.me_subdevice_io_stream_status =
+           me4600_ao_io_stream_status;
+       subdevice->base.me_subdevice_io_stream_stop = me4600_ao_io_stream_stop;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_ao_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_ao_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_ao_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_subdevice_caps_args =
+           me4600_ao_query_subdevice_caps_args;
+       subdevice->base.me_subdevice_query_range_by_min_max =
+           me4600_ao_query_range_by_min_max;
+       subdevice->base.me_subdevice_query_number_ranges =
+           me4600_ao_query_number_ranges;
+       subdevice->base.me_subdevice_query_range_info =
+           me4600_ao_query_range_info;
+       subdevice->base.me_subdevice_query_timer = me4600_ao_query_timer;
+
+       // Prepare work queue
+       subdevice->me4600_workqueue = me4600_wq;
+
+/* workqueue API changed in kernel 2.6.20 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) )
+       INIT_WORK(&subdevice->ao_control_task, me4600_ao_work_control_task,
+                 (void *)subdevice);
+#else
+       INIT_DELAYED_WORK(&subdevice->ao_control_task,
+                         me4600_ao_work_control_task);
+#endif
+
+       if (subdevice->fifo) {  // Set speed for single operations.
+               outl(ME4600_AO_MIN_CHAN_TICKS - 1, subdevice->timer_reg);
+               subdevice->hardware_stop_delay = HZ / 10;       //100ms
+       }
+
+       return subdevice;
+}
+
+/** @brief Stop presentation. Preserve FIFOs.
+*
+* @param instance The subdevice instance (pointer).
+*/
+int inline ao_stop_immediately(me4600_ao_subdevice_t * instance)
+{
+       unsigned long cpu_flags;
+       uint32_t ctrl;
+       int timeout;
+       int i;
+
+       timeout =
+           (instance->hardware_stop_delay >
+            (HZ / 10)) ? instance->hardware_stop_delay : HZ / 10;
+       for (i = 0; i <= timeout; i++) {
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched!
+               ctrl = inl(instance->ctrl_reg);
+               ctrl |=
+                   ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
+                   | ME4600_AO_CTRL_BIT_RESET_IRQ;
+               ctrl &=
+                   ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                     ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {  // Exit.
+                       break;
+               }
+               //Still working!
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+       }
+
+       if (i > timeout) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               return ME_ERRNO_INTERNAL;
+       }
+       return ME_ERRNO_SUCCESS;
+}
+
+/** @brief Copy data from circular buffer to fifo (fast) in wraparound.
+* @note This is time critical function. Checking is done at begining and end only.
+* @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly.
+*
+* @param instance The subdevice instance (pointer).
+* @param count Maximum number of copied data.
+* @param start_pos Position of the firs value in buffer.
+*
+* @return On success: Number of copied data.
+* @return On error/success: 0. No datas were copied => no data in buffer.
+* @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW.
+*/
+int inline ao_write_data_wraparound(me4600_ao_subdevice_t * instance, int count,
+                                   int start_pos)
+{                              /// @note This is time critical function!
+       uint32_t status;
+       uint32_t value;
+       int pos =
+           (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
+       int local_count = count;
+       int i = 1;
+
+       if (count <= 0) {       //Wrong count!
+               return 0;
+       }
+
+       while (i < local_count) {
+               //Get value from buffer
+               value = *(instance->circ_buf.buf + pos);
+               //Prepare it
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+
+               pos++;
+               pos &= instance->circ_buf.mask;
+               if (pos == instance->circ_buf.head) {
+                       pos = instance->circ_buf.tail;
+               }
+               i++;
+       }
+
+       status = inl(instance->status_reg);
+       if (!(status & ME4600_AO_STATUS_BIT_FF)) {      //FIFO is full before all datas were copied!
+               PERROR("FIFO was full before all datas were copied! idx=%d\n",
+                      instance->ao_idx);
+               return -ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+       } else {                //Add last value
+               value = *(instance->circ_buf.buf + pos);
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+       }
+
+       PINFO("WRAPAROUND LOADED %d values. idx=%d\n", local_count,
+             instance->ao_idx);
+       return local_count;
+}
+
+/** @brief Copy data from software buffer to fifo (fast).
+* @note This is time critical function. Checking is done at begining and end only.
+* @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly.
+*
+* @param instance The subdevice instance (pointer).
+* @param count Maximum number of copied data.
+* @param start_pos Position of the firs value in buffer.
+*
+* @return On success: Number of copied data.
+* @return On error/success: 0. No datas were copied => no data in buffer.
+* @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW.
+*/
+int inline ao_write_data(me4600_ao_subdevice_t * instance, int count,
+                        int start_pos)
+{                              /// @note This is time critical function!
+       uint32_t status;
+       uint32_t value;
+       int pos =
+           (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
+       int local_count = count;
+       int max_count;
+       int i = 1;
+
+       if (count <= 0) {       //Wrong count!
+               return 0;
+       }
+
+       max_count = me_circ_buf_values(&instance->circ_buf) - start_pos;
+       if (max_count <= 0) {   //No data to copy!
+               return 0;
+       }
+
+       if (max_count < count) {
+               local_count = max_count;
+       }
+
+       while (i < local_count) {
+               //Get value from buffer
+               value = *(instance->circ_buf.buf + pos);
+               //Prepare it
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+
+               pos++;
+               pos &= instance->circ_buf.mask;
+               i++;
+       }
+
+       status = inl(instance->status_reg);
+       if (!(status & ME4600_AO_STATUS_BIT_FF)) {      //FIFO is full before all datas were copied!
+               PERROR("FIFO was full before all datas were copied! idx=%d\n",
+                      instance->ao_idx);
+               return -ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+       } else {                //Add last value
+               value = *(instance->circ_buf.buf + pos);
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+       }
+
+       PINFO("FAST LOADED %d values. idx=%d\n", local_count, instance->ao_idx);
+       return local_count;
+}
+
+/** @brief Copy data from software buffer to fifo (slow).
+* @note This is slow function that copy all data from buffer to FIFO with full control.
+*
+* @param instance The subdevice instance (pointer).
+* @param count Maximum number of copied data.
+* @param start_pos Position of the firs value in buffer.
+*
+* @return On success: Number of copied values.
+* @return On error/success: 0. FIFO was full at begining.
+* @return On error: -ME_ERRNO_RING_BUFFER_UNDEFFLOW.
+*/
+int inline ao_write_data_pooling(me4600_ao_subdevice_t * instance, int count,
+                                int start_pos)
+{                              /// @note This is slow function!
+       uint32_t status;
+       uint32_t value;
+       int pos =
+           (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
+       int local_count = count;
+       int i;
+       int max_count;
+
+       if (count <= 0) {       //Wrong count!
+               PERROR("SLOW LOADED: Wrong count! idx=%d\n", instance->ao_idx);
+               return 0;
+       }
+
+       max_count = me_circ_buf_values(&instance->circ_buf) - start_pos;
+       if (max_count <= 0) {   //No data to copy!
+               PERROR("SLOW LOADED: No data to copy! idx=%d\n",
+                      instance->ao_idx);
+               return 0;
+       }
+
+       if (max_count < count) {
+               local_count = max_count;
+       }
+
+       for (i = 0; i < local_count; i++) {
+               status = inl(instance->status_reg);
+               if (!(status & ME4600_AO_STATUS_BIT_FF)) {      //FIFO is full!
+                       return i;
+               }
+               //Get value from buffer
+               value = *(instance->circ_buf.buf + pos);
+               //Prepare it
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+
+               pos++;
+               pos &= instance->circ_buf.mask;
+       }
+
+       PINFO("SLOW LOADED %d values. idx=%d\n", local_count, instance->ao_idx);
+       return local_count;
+}
+
+/** @brief Copy data from user space to circular buffer.
+* @param instance The subdevice instance (pointer).
+* @param count Number of datas in user space.
+* @param user_values Buffer's pointer.
+*
+* @return On success: Number of copied values.
+* @return On error: -ME_ERRNO_INTERNAL.
+*/
+int inline ao_get_data_from_user(me4600_ao_subdevice_t * instance, int count,
+                                int *user_values)
+{
+       int i, err;
+       int empty_space;
+       int copied;
+       int value;
+
+       empty_space = me_circ_buf_space(&instance->circ_buf);
+       //We have only this space free.
+       copied = (count < empty_space) ? count : empty_space;
+       for (i = 0; i < copied; i++) {  //Copy from user to buffer
+               if ((err = get_user(value, (int *)(user_values + i)))) {
+                       PERROR
+                           ("BUFFER LOADED: get_user(0x%p) return an error: %d. idx=%d\n",
+                            user_values + i, err, instance->ao_idx);
+                       return -ME_ERRNO_INTERNAL;
+               }
+               /// @note The analog output in me4600 series has size of 16 bits.
+               *(instance->circ_buf.buf + instance->circ_buf.head) =
+                   (uint16_t) value;
+               instance->circ_buf.head++;
+               instance->circ_buf.head &= instance->circ_buf.mask;
+       }
+
+       PINFO("BUFFER LOADED %d values. idx=%d\n", copied, instance->ao_idx);
+       return copied;
+}
+
+/** @brief Checking actual hardware and logical state.
+* @param instance The subdevice instance (pointer).
+*/
+static void me4600_ao_work_control_task(
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+                                              void *subdevice
+#else
+                                              struct work_struct *work
+#endif
+    )
+{
+       me4600_ao_subdevice_t *instance;
+       unsigned long cpu_flags = 0;
+       uint32_t status;
+       uint32_t ctrl;
+       uint32_t synch;
+       int reschedule = 0;
+       int signaling = 0;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       instance = (me4600_ao_subdevice_t *) subdevice;
+#else
+       instance =
+           container_of((void *)work, me4600_ao_subdevice_t, ao_control_task);
+#endif
+       PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies,
+             instance->ao_idx);
+
+       status = inl(instance->status_reg);
+       PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->status_reg - instance->reg_base, status);
+
+       switch (instance->status) {     // Checking actual mode.
+
+               // Not configured for work.
+       case ao_status_none:
+               break;
+
+               //This are stable modes. No need to do anything. (?)
+       case ao_status_single_configured:
+       case ao_status_stream_configured:
+       case ao_status_stream_fifo_error:
+       case ao_status_stream_buffer_error:
+       case ao_status_stream_error:
+               PERROR("Shouldn't be running!.\n");
+               break;
+
+       case ao_status_stream_end:
+               if (!instance->fifo) {
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+       case ao_status_single_end:
+               if (status & ME4600_AO_STATUS_BIT_FSM) {        // State machine is working but the status is set to end. Force stop.
+
+                       // Wait for stop.
+                       reschedule = 1;
+               }
+
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched!
+               ctrl = inl(instance->ctrl_reg);
+               ctrl |=
+                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP
+                   | ME4600_AO_CTRL_BIT_RESET_IRQ;
+               ctrl &=
+                   ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                     ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
+               ctrl &=
+                   ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
+                     ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+               break;
+
+               // Single modes
+       case ao_status_single_run_wait:
+       case ao_status_single_run:
+       case ao_status_single_end_wait:
+
+               if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     // State machine is not working.
+                       if (((instance->fifo)
+                            && (!(status & ME4600_AO_STATUS_BIT_EF)))
+                           || (!(instance->fifo))) {   // Single is in end state.
+                               PDEBUG("Single call has been complited.\n");
+
+                               // Set correct value for single_read();
+                               instance->single_value =
+                                   instance->single_value_in_fifo;
+
+                               // Set status as 'ao_status_single_end'
+                               instance->status = ao_status_single_end;
+
+                               // Signal the end.
+                               signaling = 1;
+                               // Wait for stop ISM.
+                               reschedule = 1;
+
+                               break;
+                       }
+               }
+               // Check timeout.
+               if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {       // Timeout
+                       PDEBUG("Timeout reached.\n");
+                       // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched!
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           ME4600_AO_CTRL_BIT_STOP |
+                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
+                           ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       ctrl &=
+                           ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                       /// Fix for timeout error.
+                       ctrl &=
+                           ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
+                             ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
+                       if (instance->fifo) {   //Disabling FIFO
+                               ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO;
+                       }
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       spin_lock(instance->preload_reg_lock);
+                       //Remove from synchronous start. Block triggering from this output.
+                       synch = inl(instance->preload_reg);
+                       synch &=
+                           ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) <<
+                             instance->ao_idx);
+                       if (!(instance->fifo)) {        // No FIFO - set to single safe mode
+                               synch |=
+                                   ME4600_AO_SYNC_HOLD << instance->ao_idx;
+                       }
+                       outl(synch, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  synch);
+                       spin_unlock(instance->preload_reg_lock);
+
+                       if (!(instance->fifo)) {        // No FIFO
+                               // Restore old settings.
+                               PDEBUG("Write old value back to register.\n");
+                               outl(instance->single_value,
+                                    instance->single_reg);
+                               PDEBUG_REG
+                                   ("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->single_reg - instance->reg_base,
+                                    instance->single_value);
+                       }
+                       // Set correct value for single_read();
+                       instance->single_value_in_fifo = instance->single_value;
+
+                       instance->status = ao_status_single_end;
+
+                       // Signal the end.
+                       signaling = 1;
+               }
+               // Wait for stop.
+               reschedule = 1;
+               break;
+
+               // Stream modes
+       case ao_status_stream_run_wait:
+               if (!instance->fifo) {
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+
+               if (status & ME4600_AO_STATUS_BIT_FSM) {        // State machine is working. Waiting for start finish.
+                       instance->status = ao_status_stream_run;
+
+                       // Signal end of this step
+                       signaling = 1;
+               } else {        // State machine is not working.
+                       if (!(status & ME4600_AO_STATUS_BIT_EF)) {      // FIFO is empty. Procedure has started and finish already!
+                               instance->status = ao_status_stream_end;
+
+                               // Signal the end.
+                               signaling = 1;
+                               // Wait for stop.
+                               reschedule = 1;
+                               break;
+                       }
+               }
+
+               // Check timeout.
+               if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {       // Timeout
+                       PDEBUG("Timeout reached.\n");
+                       // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched!
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           ME4600_AO_CTRL_BIT_STOP |
+                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
+                           ME4600_AO_CTRL_BIT_RESET_IRQ;
+                       ctrl &=
+                           ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+                       spin_lock(instance->preload_reg_lock);
+                       //Remove from synchronous start. Block triggering from this output.
+                       synch = inl(instance->preload_reg);
+                       synch &=
+                           ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) <<
+                             instance->ao_idx);
+                       outl(synch, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  synch);
+                       spin_unlock(instance->preload_reg_lock);
+
+                       instance->status = ao_status_stream_end;
+
+                       // Signal the end.
+                       signaling = 1;
+               }
+               // Wait for stop.
+               reschedule = 1;
+               break;
+
+       case ao_status_stream_run:
+               if (!instance->fifo) {
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+
+               if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     // State machine is not working. This is an error.
+                       // BROKEN PIPE!
+                       if (!(status & ME4600_AO_STATUS_BIT_EF)) {      // FIFO is empty.
+                               if (me_circ_buf_values(&instance->circ_buf)) {  // Software buffer is not empty.
+                                       if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) { //Finishing work. Requed data shown.
+                                               PDEBUG
+                                                   ("ISM stoped. No data in FIFO. Buffer is not empty.\n");
+                                               instance->status =
+                                                   ao_status_stream_end;
+                                       } else {
+                                               PERROR
+                                                   ("Output stream has been broken. ISM stoped. No data in FIFO. Buffer is not empty.\n");
+                                               instance->status =
+                                                   ao_status_stream_buffer_error;
+                                       }
+                               } else {        // Software buffer is empty.
+                                       PDEBUG
+                                           ("ISM stoped. No data in FIFO. Buffer is empty.\n");
+                                       instance->status = ao_status_stream_end;
+                               }
+                       } else {        // There are still datas in FIFO.
+                               if (me_circ_buf_values(&instance->circ_buf)) {  // Software buffer is not empty.
+                                       PERROR
+                                           ("Output stream has been broken. ISM stoped but some data in FIFO and buffer.\n");
+                               } else {        // Software buffer is empty.
+                                       PERROR
+                                           ("Output stream has been broken. ISM stoped but some data in FIFO. Buffer is empty.\n");
+                               }
+                               instance->status = ao_status_stream_fifo_error;
+
+                       }
+
+                       // Signal the failure.
+                       signaling = 1;
+                       break;
+               }
+               // Wait for stop.
+               reschedule = 1;
+               break;
+
+       case ao_status_stream_end_wait:
+               if (!instance->fifo) {
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+
+               if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     // State machine is not working. Waiting for stop finish.
+                       instance->status = ao_status_stream_end;
+                       signaling = 1;
+               }
+               // State machine is working.
+               reschedule = 1;
+               break;
+
+       default:
+               PERROR_CRITICAL("Status is in wrong state (%d)!\n",
+                               instance->status);
+               instance->status = ao_status_stream_error;
+               // Signal the end.
+               signaling = 1;
+               break;
+
+       }
+
+       if (signaling) {        //Signal it.
+               wake_up_interruptible_all(&instance->wait_queue);
+       }
+
+       if (instance->ao_control_task_flag && reschedule) {     // Reschedule task
+               queue_delayed_work(instance->me4600_workqueue,
+                                  &instance->ao_control_task, 1);
+       } else {
+               PINFO("<%s> Ending control task.\n", __FUNCTION__);
+       }
+
+}
+#else
+/// @note SPECIAL BUILD FOR BOSCH
+/// @author Guenter Gebhardt
+static int me4600_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t tmp;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER spin_lock_irqsave(&instance->subdevice_lock, status);
+       spin_lock(instance->preload_reg_lock);
+       tmp = inl(instance->preload_reg);
+       tmp &= ~(0x10001 << instance->ao_idx);
+       outl(tmp, instance->preload_reg);
+       *instance->preload_flags &= ~(0x1 << instance->ao_idx);
+       spin_unlock(instance->preload_reg_lock);
+
+       tmp = inl(instance->ctrl_reg);
+       tmp |= ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+       outl(tmp, instance->ctrl_reg);
+
+       while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ;
+
+       outl(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP,
+            instance->ctrl_reg);
+
+       outl(0x8000, instance->single_reg);
+
+       instance->single_value = 0x8000;
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t tmp;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER
+           spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       if (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) {
+               PERROR("Subdevice is busy.\n");
+               err = ME_ERRNO_SUBDEVICE_BUSY;
+               goto ERROR;
+       }
+
+       if (channel == 0) {
+               if (single_config == 0) {
+                       if (ref == ME_REF_AO_GROUND) {
+                               if (trig_chan == ME_TRIG_CHAN_DEFAULT) {
+                                       if (trig_type == ME_TRIG_TYPE_SW) {
+                                               tmp = inl(instance->ctrl_reg);
+                                               tmp |=
+                                                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                               outl(tmp, instance->ctrl_reg);
+                                               tmp =
+                                                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                               outl(tmp, instance->ctrl_reg);
+
+                                               spin_lock(instance->
+                                                         preload_reg_lock);
+                                               tmp =
+                                                   inl(instance->preload_reg);
+                                               tmp &=
+                                                   ~(0x10001 << instance->
+                                                     ao_idx);
+                                               outl(tmp,
+                                                    instance->preload_reg);
+                                               *instance->preload_flags &=
+                                                   ~(0x1 << instance->ao_idx);
+                                               spin_unlock(instance->
+                                                           preload_reg_lock);
+                                       } else if (trig_type ==
+                                                  ME_TRIG_TYPE_EXT_DIGITAL) {
+                                               if (trig_edge ==
+                                                   ME_TRIG_EDGE_RISING) {
+                                                       tmp =
+                                                           inl(instance->
+                                                               ctrl_reg);
+                                                       tmp |=
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                                       tmp =
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                               } else if (trig_edge ==
+                                                          ME_TRIG_EDGE_FALLING)
+                                               {
+                                                       tmp =
+                                                           inl(instance->
+                                                               ctrl_reg);
+                                                       tmp |=
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                                       tmp =
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                               } else if (trig_edge ==
+                                                          ME_TRIG_EDGE_ANY) {
+                                                       tmp =
+                                                           inl(instance->
+                                                               ctrl_reg);
+                                                       tmp |=
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                                       tmp =
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                               } else {
+                                                       PERROR
+                                                           ("Invalid trigger edge.\n");
+                                                       err =
+                                                           ME_ERRNO_INVALID_TRIG_EDGE;
+                                                       goto ERROR;
+                                               }
+
+                                               spin_lock(instance->
+                                                         preload_reg_lock);
+
+                                               tmp =
+                                                   inl(instance->preload_reg);
+                                               tmp &=
+                                                   ~(0x10001 << instance->
+                                                     ao_idx);
+                                               tmp |= 0x1 << instance->ao_idx;
+                                               outl(tmp,
+                                                    instance->preload_reg);
+                                               *instance->preload_flags &=
+                                                   ~(0x1 << instance->ao_idx);
+                                               spin_unlock(instance->
+                                                           preload_reg_lock);
+                                       } else {
+                                               PERROR
+                                                   ("Invalid trigger type.\n");
+                                               err =
+                                                   ME_ERRNO_INVALID_TRIG_TYPE;
+                                               goto ERROR;
+                                       }
+                               } else if (trig_chan ==
+                                          ME_TRIG_CHAN_SYNCHRONOUS) {
+                                       if (trig_type == ME_TRIG_TYPE_SW) {
+                                               tmp = inl(instance->ctrl_reg);
+                                               tmp |=
+                                                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                               outl(tmp, instance->ctrl_reg);
+                                               tmp =
+                                                   ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                               outl(tmp, instance->ctrl_reg);
+
+                                               spin_lock(instance->
+                                                         preload_reg_lock);
+                                               tmp =
+                                                   inl(instance->preload_reg);
+                                               tmp &=
+                                                   ~(0x10001 << instance->
+                                                     ao_idx);
+                                               tmp |= 0x1 << instance->ao_idx;
+                                               outl(tmp,
+                                                    instance->preload_reg);
+                                               *instance->preload_flags |=
+                                                   0x1 << instance->ao_idx;
+                                               spin_unlock(instance->
+                                                           preload_reg_lock);
+                                       } else if (trig_type ==
+                                                  ME_TRIG_TYPE_EXT_DIGITAL) {
+                                               if (trig_edge ==
+                                                   ME_TRIG_EDGE_RISING) {
+                                                       tmp =
+                                                           inl(instance->
+                                                               ctrl_reg);
+                                                       tmp |=
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                                       tmp =
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                               } else if (trig_edge ==
+                                                          ME_TRIG_EDGE_FALLING)
+                                               {
+                                                       tmp =
+                                                           inl(instance->
+                                                               ctrl_reg);
+                                                       tmp |=
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                                       tmp =
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                               } else if (trig_edge ==
+                                                          ME_TRIG_EDGE_ANY) {
+                                                       tmp =
+                                                           inl(instance->
+                                                               ctrl_reg);
+                                                       tmp |=
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                                       tmp =
+                                                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE
+                                                           |
+                                                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                                                       outl(tmp,
+                                                            instance->
+                                                            ctrl_reg);
+                                               } else {
+                                                       PERROR
+                                                           ("Invalid trigger edge.\n");
+                                                       err =
+                                                           ME_ERRNO_INVALID_TRIG_EDGE;
+                                                       goto ERROR;
+                                               }
+
+                                               spin_lock(instance->
+                                                         preload_reg_lock);
+
+                                               tmp =
+                                                   inl(instance->preload_reg);
+                                               tmp |=
+                                                   0x10001 << instance->ao_idx;
+                                               outl(tmp,
+                                                    instance->preload_reg);
+                                               *instance->preload_flags &=
+                                                   ~(0x1 << instance->ao_idx);
+                                               spin_unlock(instance->
+                                                           preload_reg_lock);
+                                       } else {
+                                               PERROR
+                                                   ("Invalid trigger type.\n");
+                                               err =
+                                                   ME_ERRNO_INVALID_TRIG_TYPE;
+                                               goto ERROR;
+                                       }
+                               } else {
+                                       PERROR
+                                           ("Invalid trigger channel specified.\n");
+                                       err = ME_ERRNO_INVALID_REF;
+                                       goto ERROR;
+                               }
+                       } else {
+                               PERROR("Invalid analog reference specified.\n");
+                               err = ME_ERRNO_INVALID_REF;
+                               goto ERROR;
+                       }
+               } else {
+                       PERROR("Invalid single config specified.\n");
+                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       goto ERROR;
+               }
+       } else {
+               PERROR("Invalid channel number specified.\n");
+               err = ME_ERRNO_INVALID_CHANNEL;
+               goto ERROR;
+       }
+
+      ERROR:
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long tmp;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       if (channel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER
+           spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       tmp = inl(instance->ctrl_reg);
+
+       if (tmp & 0x3) {
+               PERROR("Not in single mode.\n");
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+       } else {
+               *value = instance->single_value;
+       }
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long mask = 0;
+       unsigned long tmp;
+       unsigned long cpu_flags;
+       int i;
+       wait_queue_head_t queue;
+       unsigned long j;
+       unsigned long delay = 0;
+
+       PDEBUG("executed.\n");
+
+       init_waitqueue_head(&queue);
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       if (channel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       ME_SUBDEVICE_ENTER
+           spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       tmp = inl(instance->ctrl_reg);
+
+       if (tmp & 0x3) {
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+               PERROR("Not in single mode.\n");
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+               goto ERROR;
+       }
+
+       if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) {
+               outl(value, instance->single_reg);
+               instance->single_value = value;
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+               if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+                       j = jiffies;
+
+                       while (inl(instance->status_reg) &
+                              ME4600_AO_STATUS_BIT_FSM) {
+                               interruptible_sleep_on_timeout(&queue, 1);
+
+                               if (signal_pending(current)) {
+                                       PERROR
+                                           ("Wait on external trigger interrupted by signal.\n");
+                                       err = ME_ERRNO_SIGNAL;
+                                       goto ERROR;
+                               }
+
+                               if (delay && ((jiffies - j) > delay)) {
+                                       PERROR("Timeout reached.\n");
+                                       err = ME_ERRNO_TIMEOUT;
+                                       goto ERROR;
+                               }
+                       }
+               }
+       } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx))
+                  == (0x10001 << instance->ao_idx)) {
+               if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {
+                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+                       outl(tmp, instance->ctrl_reg);
+                       outl(value, instance->single_reg);
+                       instance->single_value = value;
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+                               j = jiffies;
+
+                               while (inl(instance->status_reg) &
+                                      ME4600_AO_STATUS_BIT_FSM) {
+                                       interruptible_sleep_on_timeout(&queue,
+                                                                      1);
+
+                                       if (signal_pending(current)) {
+                                               PERROR
+                                                   ("Wait on external trigger interrupted by signal.\n");
+                                               err = ME_ERRNO_SIGNAL;
+                                               goto ERROR;
+                                       }
+
+                                       if (delay && ((jiffies - j) > delay)) {
+                                               PERROR("Timeout reached.\n");
+                                               err = ME_ERRNO_TIMEOUT;
+                                               goto ERROR;
+                                       }
+                               }
+                       }
+               } else {
+                       outl(value, instance->single_reg);
+                       instance->single_value = value;
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               }
+       } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx))
+                  == (0x1 << instance->ao_idx)) {
+               outl(value, instance->single_reg);
+               instance->single_value = value;
+
+               PDEBUG("Synchronous SW, flags = 0x%X.\n", flags);
+
+               if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {
+                       PDEBUG("Trigger synchronous SW.\n");
+                       spin_lock(instance->preload_reg_lock);
+                       tmp = inl(instance->preload_reg);
+
+                       for (i = 0; i < ME4600_AO_MAX_SUBDEVICES; i++) {
+                               if ((*instance->preload_flags & (0x1 << i))) {
+                                       if ((tmp & (0x10001 << i)) ==
+                                           (0x1 << i)) {
+                                               mask |= 0x1 << i;
+                                       }
+                               }
+                       }
+
+                       tmp &= ~(mask);
+
+                       outl(tmp, instance->preload_reg);
+                       spin_unlock(instance->preload_reg_lock);
+               }
+
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       } else {
+               outl(value, instance->single_reg);
+               instance->single_value = value;
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       }
+
+      ERROR:
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long ctrl;
+       unsigned long tmp;
+       unsigned long cpu_flags;
+       uint64_t conv_ticks;
+       unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow;
+       unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       conv_ticks =
+           (uint64_t) conv_start_ticks_low +
+           ((uint64_t) conv_start_ticks_high << 32);
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       ME_SUBDEVICE_ENTER
+           spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       if ((inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_FSM) {
+               PERROR("Subdevice is busy.\n");
+               err = ME_ERRNO_SUBDEVICE_BUSY;
+               goto ERROR;
+       }
+
+       ctrl = inl(instance->ctrl_reg);
+       ctrl |= ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+       outl(ctrl, instance->ctrl_reg);
+       ctrl = ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+       outl(ctrl, instance->ctrl_reg);
+
+       if (count != 1) {
+               PERROR("Invalid stream configuration list count specified.\n");
+               err = ME_ERRNO_INVALID_CONFIG_LIST_COUNT;
+               goto ERROR;
+       }
+
+       if (config_list[0].iChannel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               err = ME_ERRNO_INVALID_CHANNEL;
+               goto ERROR;
+       }
+
+       if (config_list[0].iStreamConfig != 0) {
+               PERROR("Invalid stream config specified.\n");
+               err = ME_ERRNO_INVALID_STREAM_CONFIG;
+               goto ERROR;
+       }
+
+       if (config_list[0].iRef != ME_REF_AO_GROUND) {
+               PERROR("Invalid analog reference.\n");
+               err = ME_ERRNO_INVALID_REF;
+               goto ERROR;
+       }
+
+       if ((trigger->iAcqStartTicksLow != 0)
+           || (trigger->iAcqStartTicksHigh != 0)) {
+               PERROR
+                   ("Invalid acquisition start trigger argument specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_START_ARG;
+               goto ERROR;
+       }
+
+       switch (trigger->iAcqStartTrigType) {
+
+       case ME_TRIG_TYPE_SW:
+               break;
+
+       case ME_TRIG_TYPE_EXT_DIGITAL:
+               ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               switch (trigger->iAcqStartTrigEdge) {
+
+               case ME_TRIG_EDGE_RISING:
+                       break;
+
+               case ME_TRIG_EDGE_FALLING:
+                       ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
+
+                       break;
+
+               case ME_TRIG_EDGE_ANY:
+                       ctrl |=
+                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
+                           ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+
+                       break;
+
+               default:
+                       PERROR
+                           ("Invalid acquisition start trigger edge specified.\n");
+
+                       err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+
+                       goto ERROR;
+
+                       break;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid acquisition start trigger type specified.\n");
+
+               err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;
+
+               goto ERROR;
+
+               break;
+       }
+
+       switch (trigger->iScanStartTrigType) {
+
+       case ME_TRIG_TYPE_FOLLOW:
+               break;
+
+       default:
+               PERROR("Invalid scan start trigger type specified.\n");
+
+               err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+
+               goto ERROR;
+
+               break;
+       }
+
+       switch (trigger->iConvStartTrigType) {
+
+       case ME_TRIG_TYPE_TIMER:
+               if ((conv_ticks < ME4600_AO_MIN_CHAN_TICKS)
+                   || (conv_ticks > ME4600_AO_MAX_CHAN_TICKS)) {
+                       PERROR
+                           ("Invalid conv start trigger argument specified.\n");
+                       err = ME_ERRNO_INVALID_CONV_START_ARG;
+                       goto ERROR;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid conv start trigger type specified.\n");
+
+               err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+
+               goto ERROR;
+
+               break;
+       }
+
+       /* Preset to hardware wraparound mode */
+       instance->flags &= ~(ME4600_AO_FLAGS_SW_WRAP_MODE_MASK);
+
+       switch (trigger->iScanStopTrigType) {
+
+       case ME_TRIG_TYPE_NONE:
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       /* Set flags to indicate usage of software mode. */
+                       instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_INF;
+                       instance->wrap_count = 0;
+                       instance->wrap_remaining = 0;
+               }
+
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       if (trigger->iScanStopCount <= 0) {
+                               PERROR("Invalid scan stop count specified.\n");
+                               err = ME_ERRNO_INVALID_SCAN_STOP_ARG;
+                               goto ERROR;
+                       }
+
+                       /* Set flags to indicate usage of software mode. */
+                       instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_FIN;
+                       instance->wrap_count = trigger->iScanStopCount;
+                       instance->wrap_remaining = trigger->iScanStopCount;
+               } else {
+                       PERROR("Invalid scan stop trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+                       goto ERROR;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid scan stop trigger type specified.\n");
+
+               err = ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE;
+
+               goto ERROR;
+
+               break;
+       }
+
+       switch (trigger->iAcqStopTrigType) {
+
+       case ME_TRIG_TYPE_NONE:
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
+                       PERROR("Invalid acq stop trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+                       goto ERROR;
+               }
+
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       if (trigger->iAcqStopCount <= 0) {
+                               PERROR("Invalid acq stop count specified.\n");
+                               err = ME_ERRNO_INVALID_ACQ_STOP_ARG;
+                               goto ERROR;
+                       }
+
+                       /* Set flags to indicate usage of software mode. */
+                       instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_FIN;
+                       instance->wrap_count = trigger->iAcqStopCount;
+                       instance->wrap_remaining = trigger->iAcqStopCount;
+               } else {
+                       PERROR("Invalid acp stop trigger type specified.\n");
+                       err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+                       goto ERROR;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid acq stop trigger type specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+               goto ERROR;
+               break;
+       }
+
+       switch (trigger->iAcqStartTrigChan) {
+
+       case ME_TRIG_CHAN_DEFAULT:
+               spin_lock(instance->preload_reg_lock);
+               tmp = inl(instance->preload_reg);
+               tmp &= ~(0x10001 << instance->ao_idx);
+               outl(tmp, instance->preload_reg);
+               spin_unlock(instance->preload_reg_lock);
+
+               break;
+
+       case ME_TRIG_CHAN_SYNCHRONOUS:
+               if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) {
+                       spin_lock(instance->preload_reg_lock);
+                       tmp = inl(instance->preload_reg);
+                       tmp &= ~(0x10001 << instance->ao_idx);
+                       outl(tmp, instance->preload_reg);
+                       tmp |= 0x1 << instance->ao_idx;
+                       outl(tmp, instance->preload_reg);
+                       spin_unlock(instance->preload_reg_lock);
+               } else {
+                       ctrl &= ~(ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                       spin_lock(instance->preload_reg_lock);
+                       tmp = inl(instance->preload_reg);
+                       tmp &= ~(0x10001 << instance->ao_idx);
+                       outl(tmp, instance->preload_reg);
+                       tmp |= 0x10000 << instance->ao_idx;
+                       outl(tmp, instance->preload_reg);
+                       spin_unlock(instance->preload_reg_lock);
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid acq start trigger channel specified.\n");
+               err = ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN;
+               goto ERROR;
+
+               break;
+       }
+
+       outl(conv_ticks - 2, instance->timer_reg);
+
+       if (flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) {
+               if (instance->ao_idx == 3) {
+                       ctrl |= ME4600_AO_CTRL_BIT_ENABLE_DO;
+               } else {
+                       err = ME_ERRNO_INVALID_FLAGS;
+                       goto ERROR;
+               }
+       } else {
+               if (instance->ao_idx == 3) {
+                       ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_DO;
+               }
+       }
+
+       /* Set hardware mode. */
+       if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+               ctrl |= ME4600_AO_CTRL_BIT_MODE_0;
+       } else {
+               ctrl |= ME4600_AO_CTRL_BIT_MODE_1;
+       }
+
+       PDEBUG("Preload word = 0x%X.\n", inl(instance->preload_reg));
+
+       PDEBUG("Ctrl word = 0x%lX.\n", ctrl);
+       outl(ctrl, instance->ctrl_reg); // Write the control word
+
+      ERROR:
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       long j;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       *count = 0;
+
+       ME_SUBDEVICE_ENTER;
+
+       if (t) {
+               j = jiffies;
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((me_circ_buf_space
+                                                  (&instance->circ_buf))
+                                                 || !(inl(instance->status_reg)
+                                                      &
+                                                      ME4600_AO_STATUS_BIT_FSM)),
+                                                t);
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
+                       PERROR("AO subdevice is not running.\n");
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+               } else if (signal_pending(current)) {
+                       PERROR("Wait on values interrupted from signal.\n");
+                       err = ME_ERRNO_SIGNAL;
+               } else if ((jiffies - j) >= t) {
+                       PERROR("Wait on values timed out.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               } else {
+                       *count = me_circ_buf_space(&instance->circ_buf);
+               }
+       } else {
+               wait_event_interruptible(instance->wait_queue,
+                                        ((me_circ_buf_space
+                                          (&instance->circ_buf))
+                                         || !(inl(instance->status_reg) &
+                                              ME4600_AO_STATUS_BIT_FSM)));
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
+                       PERROR("AO subdevice is not running.\n");
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+               } else if (signal_pending(current)) {
+                       PERROR("Wait on values interrupted from signal.\n");
+                       err = ME_ERRNO_SIGNAL;
+               } else {
+                       *count = me_circ_buf_space(&instance->circ_buf);
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static void stop_immediately(me4600_ao_subdevice_t * instance)
+{
+       unsigned long cpu_flags;
+       uint32_t tmp;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       tmp = inl(instance->ctrl_reg);
+       tmp |= ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+       outl(tmp, instance->ctrl_reg);
+
+       while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ;
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+}
+
+static int me4600_ao_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags = 0;
+       unsigned long ref;
+       unsigned long tmp;
+       unsigned long delay = 0;
+       wait_queue_head_t queue;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       init_waitqueue_head(&queue);
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       ME_SUBDEVICE_ENTER
+           spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       tmp = inl(instance->ctrl_reg);
+
+       switch (tmp & (ME4600_AO_CTRL_MASK_MODE)) {
+
+       case 0:         // Single mode
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+               PERROR("Subdevice is configured in single mode.\n");
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+               goto ERROR;
+
+       case 1:         // Wraparound mode
+               if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) {  // Normal wraparound with external trigger
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME4600_AO_CTRL_BIT_STOP |
+                             ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+
+                       outl(tmp, instance->ctrl_reg);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (start_mode == ME_START_MODE_BLOCKING) {
+                               init_waitqueue_head(&queue);
+
+                               if (delay) {
+                                       ref = jiffies;
+
+                                       while (!
+                                              (inl(instance->status_reg) &
+                                               ME4600_AO_STATUS_BIT_FSM)) {
+                                               interruptible_sleep_on_timeout
+                                                   (&queue, 1);
+
+                                               if (signal_pending(current)) {
+                                                       PERROR
+                                                           ("Wait on start of state machine interrupted.\n");
+                                                       stop_immediately
+                                                           (instance);
+                                                       err = ME_ERRNO_SIGNAL;
+                                                       goto ERROR;
+                                               }
+
+                                               if (((jiffies - ref) >= delay)) {
+                                                       PERROR
+                                                           ("Timeout reached.\n");
+                                                       stop_immediately
+                                                           (instance);
+                                                       err = ME_ERRNO_TIMEOUT;
+                                                       goto ERROR;
+                                               }
+                                       }
+                               } else {
+                                       while (!
+                                              (inl(instance->status_reg) &
+                                               ME4600_AO_STATUS_BIT_FSM)) {
+                                               interruptible_sleep_on_timeout
+                                                   (&queue, 1);
+
+                                               if (signal_pending(current)) {
+                                                       PERROR
+                                                           ("Wait on start of state machine interrupted.\n");
+                                                       stop_immediately
+                                                           (instance);
+                                                       err = ME_ERRNO_SIGNAL;
+                                                       goto ERROR;
+                                               }
+                                       }
+                               }
+                       } else if (start_mode == ME_START_MODE_NONBLOCKING) {
+                       } else {
+                               PERROR("Invalid start mode specified.\n");
+                               err = ME_ERRNO_INVALID_START_MODE;
+                               goto ERROR;
+                       }
+               } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x10000 << instance->ao_idx)) {     // Synchronous with external trigger
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
+                               tmp |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
+                               tmp &=
+                                   ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                                     ME4600_AO_CTRL_BIT_STOP |
+                                     ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+                               outl(tmp, instance->ctrl_reg);
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+
+                               if (start_mode == ME_START_MODE_BLOCKING) {
+                                       init_waitqueue_head(&queue);
+
+                                       if (delay) {
+                                               ref = jiffies;
+
+                                               while (!
+                                                      (inl
+                                                       (instance->
+                                                        status_reg) &
+                                                       ME4600_AO_STATUS_BIT_FSM))
+                                               {
+                                                       interruptible_sleep_on_timeout
+                                                           (&queue, 1);
+
+                                                       if (signal_pending
+                                                           (current)) {
+                                                               PERROR
+                                                                   ("Wait on start of state machine interrupted.\n");
+                                                               stop_immediately
+                                                                   (instance);
+                                                               err =
+                                                                   ME_ERRNO_SIGNAL;
+                                                               goto ERROR;
+                                                       }
+
+                                                       if (((jiffies - ref) >=
+                                                            delay)) {
+                                                               PERROR
+                                                                   ("Timeout reached.\n");
+                                                               stop_immediately
+                                                                   (instance);
+                                                               err =
+                                                                   ME_ERRNO_TIMEOUT;
+                                                               goto ERROR;
+                                                       }
+                                               }
+                                       } else {
+                                               while (!
+                                                      (inl
+                                                       (instance->
+                                                        status_reg) &
+                                                       ME4600_AO_STATUS_BIT_FSM))
+                                               {
+                                                       interruptible_sleep_on_timeout
+                                                           (&queue, 1);
+
+                                                       if (signal_pending
+                                                           (current)) {
+                                                               PERROR
+                                                                   ("Wait on start of state machine interrupted.\n");
+                                                               stop_immediately
+                                                                   (instance);
+                                                               err =
+                                                                   ME_ERRNO_SIGNAL;
+                                                               goto ERROR;
+                                                       }
+                                               }
+                                       }
+                               } else if (start_mode ==
+                                          ME_START_MODE_NONBLOCKING) {
+                               } else {
+                                       PERROR
+                                           ("Invalid start mode specified.\n");
+                                       err = ME_ERRNO_INVALID_START_MODE;
+                                       goto ERROR;
+                               }
+                       } else {
+                               tmp &=
+                                   ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                                     ME4600_AO_CTRL_BIT_STOP |
+                                     ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+                               outl(tmp, instance->ctrl_reg);
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                       }
+               } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x1 << instance->ao_idx)) { // Synchronous wraparound with sw trigger
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME4600_AO_CTRL_BIT_STOP |
+                             ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+
+                       outl(tmp, instance->ctrl_reg);
+
+                       if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
+                               outl(0x8000, instance->single_reg);
+                               instance->single_value = 0x8000;
+                       }
+
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               } else {        // Software start
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME4600_AO_CTRL_BIT_STOP |
+                             ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+
+                       outl(tmp, instance->ctrl_reg);
+
+                       outl(0x8000, instance->single_reg);
+                       instance->single_value = 0x8000;
+
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               }
+
+               break;
+
+       case 2:         // Continuous mode
+               if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) {  // Externally triggered
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_STOP |
+                             ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(tmp, instance->ctrl_reg);
+                       instance->wrap_remaining = instance->wrap_count;
+                       instance->circ_buf.tail = 0;
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (start_mode == ME_START_MODE_BLOCKING) {
+                               init_waitqueue_head(&queue);
+
+                               if (delay) {
+                                       ref = jiffies;
+
+                                       while (!
+                                              (inl(instance->status_reg) &
+                                               ME4600_AO_STATUS_BIT_FSM)) {
+                                               interruptible_sleep_on_timeout
+                                                   (&queue, 1);
+
+                                               if (signal_pending(current)) {
+                                                       PERROR
+                                                           ("Wait on start of state machine interrupted.\n");
+                                                       stop_immediately
+                                                           (instance);
+                                                       err = ME_ERRNO_SIGNAL;
+                                                       goto ERROR;
+                                               }
+
+                                               if (((jiffies - ref) >= delay)) {
+                                                       PERROR
+                                                           ("Timeout reached.\n");
+                                                       stop_immediately
+                                                           (instance);
+                                                       err = ME_ERRNO_TIMEOUT;
+                                                       goto ERROR;
+                                               }
+                                       }
+                               } else {
+                                       while (!
+                                              (inl(instance->status_reg) &
+                                               ME4600_AO_STATUS_BIT_FSM)) {
+                                               interruptible_sleep_on_timeout
+                                                   (&queue, 1);
+
+                                               if (signal_pending(current)) {
+                                                       PERROR
+                                                           ("Wait on start of state machine interrupted.\n");
+                                                       stop_immediately
+                                                           (instance);
+                                                       err = ME_ERRNO_SIGNAL;
+                                                       goto ERROR;
+                                               }
+                                       }
+                               }
+                       } else if (start_mode == ME_START_MODE_NONBLOCKING) {
+                               /* Do nothing */
+                       } else {
+                               PERROR("Invalid start mode specified.\n");
+                               stop_immediately(instance);
+                               err = ME_ERRNO_INVALID_START_MODE;
+                               goto ERROR;
+                       }
+               } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x10000 << instance->ao_idx)) {     // Synchronous with external trigger
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
+                               tmp |=
+                                   ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG |
+                                   ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                               tmp &=
+                                   ~(ME4600_AO_CTRL_BIT_STOP |
+                                     ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+                               outl(tmp, instance->ctrl_reg);
+                               instance->wrap_remaining = instance->wrap_count;
+                               instance->circ_buf.tail = 0;
+
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+
+                               if (start_mode == ME_START_MODE_BLOCKING) {
+                                       init_waitqueue_head(&queue);
+
+                                       if (delay) {
+                                               ref = jiffies;
+
+                                               while (!
+                                                      (inl
+                                                       (instance->
+                                                        status_reg) &
+                                                       ME4600_AO_STATUS_BIT_FSM))
+                                               {
+                                                       interruptible_sleep_on_timeout
+                                                           (&queue, 1);
+
+                                                       if (signal_pending
+                                                           (current)) {
+                                                               PERROR
+                                                                   ("Wait on start of state machine interrupted.\n");
+                                                               stop_immediately
+                                                                   (instance);
+                                                               err =
+                                                                   ME_ERRNO_SIGNAL;
+                                                               goto ERROR;
+                                                       }
+
+                                                       if (((jiffies - ref) >=
+                                                            delay)) {
+                                                               PERROR
+                                                                   ("Timeout reached.\n");
+                                                               stop_immediately
+                                                                   (instance);
+                                                               err =
+                                                                   ME_ERRNO_TIMEOUT;
+                                                               goto ERROR;
+                                                       }
+                                               }
+                                       } else {
+                                               while (!
+                                                      (inl
+                                                       (instance->
+                                                        status_reg) &
+                                                       ME4600_AO_STATUS_BIT_FSM))
+                                               {
+                                                       interruptible_sleep_on_timeout
+                                                           (&queue, 1);
+
+                                                       if (signal_pending
+                                                           (current)) {
+                                                               PERROR
+                                                                   ("Wait on start of state machine interrupted.\n");
+                                                               stop_immediately
+                                                                   (instance);
+                                                               err =
+                                                                   ME_ERRNO_SIGNAL;
+                                                               goto ERROR;
+                                                       }
+                                               }
+                                       }
+                               } else if (start_mode ==
+                                          ME_START_MODE_NONBLOCKING) {
+                               } else {
+                                       PERROR
+                                           ("Invalid start mode specified.\n");
+                                       stop_immediately(instance);
+                                       err = ME_ERRNO_INVALID_START_MODE;
+                                       goto ERROR;
+                               }
+                       } else {
+                               tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                               tmp &=
+                                   ~(ME4600_AO_CTRL_BIT_STOP |
+                                     ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+                               outl(tmp, instance->ctrl_reg);
+                               instance->wrap_remaining = instance->wrap_count;
+                               instance->circ_buf.tail = 0;
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                       }
+               } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x1 << instance->ao_idx)) { // Synchronous wraparound with sw trigger
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_STOP |
+                             ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       instance->wrap_remaining = instance->wrap_count;
+                       instance->circ_buf.tail = 0;
+                       PDEBUG("CTRL Reg = 0x%X.\n", inl(instance->ctrl_reg));
+                       outl(tmp, instance->ctrl_reg);
+
+                       if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
+                               outl(0x8000, instance->single_reg);
+                               instance->single_value = 0x8000;
+                       }
+
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               } else {        // Software start
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR("Conversion is already running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_STOP |
+                             ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
+
+                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(tmp, instance->ctrl_reg);
+                       outl(0x8000, instance->single_reg);
+                       instance->single_value = 0x8000;
+                       instance->wrap_remaining = instance->wrap_count;
+                       instance->circ_buf.tail = 0;
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               }
+
+               break;
+
+       default:
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+               PERROR("Invalid mode configured.\n");
+               err = ME_ERRNO_INTERNAL;
+               goto ERROR;
+       }
+
+      ERROR:
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       wait_queue_head_t queue;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       init_waitqueue_head(&queue);
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (wait == ME_WAIT_NONE) {
+               *status =
+                   (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ?
+                   ME_STATUS_BUSY : ME_STATUS_IDLE;
+               *values = me_circ_buf_space(&instance->circ_buf);
+       } else if (wait == ME_WAIT_IDLE) {
+               while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) {
+                       interruptible_sleep_on_timeout(&queue, 1);
+
+                       if (instance->flags & ME4600_AO_FLAGS_BROKEN_PIPE) {
+                               PERROR("Output stream was interrupted.\n");
+                               *status = ME_STATUS_ERROR;
+                               err = ME_ERRNO_SUCCESS;
+                               goto ERROR;
+                       }
+
+                       if (signal_pending(current)) {
+                               PERROR
+                                   ("Wait on state machine interrupted by signal.\n");
+                               *status = ME_STATUS_INVALID;
+                               err = ME_ERRNO_SIGNAL;
+                               goto ERROR;
+                       }
+               }
+
+               *status = ME_STATUS_IDLE;
+
+               *values = me_circ_buf_space(&instance->circ_buf);
+       } else {
+               PERROR("Invalid wait argument specified.\n");
+               *status = ME_STATUS_INVALID;
+               err = ME_ERRNO_INVALID_WAIT;
+               goto ERROR;
+       }
+
+      ERROR:
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me4600_ao_subdevice_t *instance;
+       unsigned long cpu_flags;
+       unsigned long tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (stop_mode == ME_STOP_MODE_IMMEDIATE) {
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               tmp = inl(instance->ctrl_reg);
+               tmp |=
+                   ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+               outl(tmp, instance->ctrl_reg);
+
+               while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ;
+
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) {
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               tmp = inl(instance->ctrl_reg);
+               tmp |= ME4600_AO_CTRL_BIT_STOP;
+               outl(tmp, instance->ctrl_reg);
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       } else {
+               PERROR("Invalid stop mode specified.\n");
+               err = ME_ERRNO_INVALID_STOP_MODE;
+               goto ERROR;
+       }
+
+      ERROR:
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ao_io_stream_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int write_mode,
+                                    int *values, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me4600_ao_subdevice_t *instance;
+       unsigned long tmp;
+       int i;
+       int value;
+       int cnt = *count;
+       int c;
+       int k;
+       int ret = 0;
+       unsigned long cpu_flags = 0;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       if (!instance->fifo) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (*count <= 0) {
+               PERROR("Invalid count of values specified.\n");
+               err = ME_ERRNO_INVALID_VALUE_COUNT;
+               goto ERROR;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       tmp = inl(instance->ctrl_reg);
+
+       switch (tmp & 0x3) {
+
+       case 1:         // Wraparound mode
+               if (instance->bosch_fw) {       // Bosch firmware
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (cnt != 7) {
+                               PERROR
+                                   ("Invalid count of values specified. 7 expected.\n");
+                               err = ME_ERRNO_INVALID_VALUE_COUNT;
+                               goto ERROR;
+                       }
+
+                       for (i = 0; i < 7; i++) {
+                               if (get_user(value, values)) {
+                                       PERROR
+                                           ("Can't copy value from user space.\n");
+                                       err = ME_ERRNO_INTERNAL;
+                                       goto ERROR;
+                               }
+
+                               if (i == 0) {
+                                       /* Maximum voltage */
+                                       value <<= 16;
+                                       value |=
+                                           inl(instance->reg_base +
+                                               0xD4) & 0xFFFF;
+                                       outl(value, instance->reg_base + 0xD4);
+                               } else if (i == 1) {
+                                       /* Minimum voltage */
+                                       value &= 0xFFFF;
+                                       value |=
+                                           inl(instance->reg_base +
+                                               0xD4) & 0xFFFF0000;
+                                       outl(value, instance->reg_base + 0xD4);
+                               } else if (i == 2) {
+                                       /* Delta up */
+                                       value <<= 16;
+                                       value |=
+                                           inl(instance->reg_base +
+                                               0xD8) & 0xFFFF;
+                                       outl(value, instance->reg_base + 0xD8);
+                               } else if (i == 3) {
+                                       /* Delta down */
+                                       value &= 0xFFFF;
+                                       value |=
+                                           inl(instance->reg_base +
+                                               0xD8) & 0xFFFF0000;
+                                       outl(value, instance->reg_base + 0xD8);
+                               } else if (i == 4) {
+                                       /* Start value */
+                                       outl(value, instance->reg_base + 0xDC);
+                               } else if (i == 5) {
+                                       /* Invert */
+                                       if (value) {
+                                               value = inl(instance->ctrl_reg);
+                                               value |= 0x100;
+                                               outl(value, instance->ctrl_reg);
+                                       } else {
+                                               value = inl(instance->ctrl_reg);
+                                               value &= ~0x100;
+                                               outl(value, instance->ctrl_reg);
+                                       }
+                               } else if (i == 6) {
+                                       /* Timer for positive ramp */
+                                       outl(value, instance->reg_base + 0xE0);
+                               }
+
+                               values++;
+                       }
+               } else {        // Normal firmware
+                       PDEBUG("Write for wraparound mode.\n");
+
+                       if (inl(instance->status_reg) &
+                           ME4600_AO_STATUS_BIT_FSM) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR
+                                   ("There is already a conversion running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp |= ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                       tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO;
+                       outl(tmp, instance->ctrl_reg);
+                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_FIFO;
+
+                       if ((*count > ME4600_AO_FIFO_COUNT) ||
+                           ((instance->
+                             flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
+                            ME4600_AO_FLAGS_SW_WRAP_MODE_FIN)) {
+                               tmp &=
+                                   ~(ME4600_AO_CTRL_BIT_MODE_0 |
+                                     ME4600_AO_CTRL_BIT_MODE_1);
+                               tmp |= ME4600_AO_CTRL_BIT_MODE_1;
+                       }
+
+                       outl(tmp, instance->ctrl_reg);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if ((*count <= ME4600_AO_FIFO_COUNT) &&
+                           ((instance->
+                             flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
+                            ME4600_AO_FLAGS_SW_WRAP_MODE_INF)) {
+                               for (i = 0; i < *count; i++) {
+                                       if (get_user(value, values + i)) {
+                                               PERROR
+                                                   ("Cannot copy value from user space.\n");
+                                               err = ME_ERRNO_INTERNAL;
+                                               goto ERROR;
+                                       }
+
+                                       if (instance->ao_idx & 0x1)
+                                               value <<= 16;
+
+                                       outl(value, instance->fifo_reg);
+                               }
+                       } else if ((*count <= ME4600_AO_CIRC_BUF_COUNT) &&
+                                  ((instance->
+                                    flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK)
+                                   == ME4600_AO_FLAGS_SW_WRAP_MODE_INF)) {
+                               for (i = 0; i < *count; i++) {
+                                       if (get_user(value, values + i)) {
+                                               PERROR
+                                                   ("Cannot copy value from user space.\n");
+                                               err = ME_ERRNO_INTERNAL;
+                                               goto ERROR;
+                                       }
+
+                                       instance->circ_buf.buf[i] = value;      /* Used to hold the values. */
+                               }
+
+                               instance->circ_buf.tail = 0;    /* Used as the current read position. */
+                               instance->circ_buf.head = *count;       /* Used as the buffer size. */
+
+                               /* Preload the FIFO. */
+
+                               for (i = 0; i < ME4600_AO_FIFO_COUNT;
+                                    i++, instance->circ_buf.tail++) {
+                                       if (instance->circ_buf.tail >=
+                                           instance->circ_buf.head)
+                                               instance->circ_buf.tail = 0;
+
+                                       if (instance->ao_idx & 0x1)
+                                               outl(instance->circ_buf.
+                                                    buf[instance->circ_buf.
+                                                        tail] << 16,
+                                                    instance->fifo_reg);
+                                       else
+                                               outl(instance->circ_buf.
+                                                    buf[instance->circ_buf.
+                                                        tail],
+                                                    instance->fifo_reg);
+                               }
+                       } else if ((*count <= ME4600_AO_CIRC_BUF_COUNT) &&
+                                  ((instance->
+                                    flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK)
+                                   == ME4600_AO_FLAGS_SW_WRAP_MODE_FIN)) {
+                               unsigned int preload_count;
+
+                               for (i = 0; i < *count; i++) {
+                                       if (get_user(value, values + i)) {
+                                               PERROR
+                                                   ("Cannot copy value from user space.\n");
+                                               err = ME_ERRNO_INTERNAL;
+                                               goto ERROR;
+                                       }
+
+                                       instance->circ_buf.buf[i] = value;      /* Used to hold the values. */
+                               }
+
+                               instance->circ_buf.tail = 0;    /* Used as the current read position. */
+                               instance->circ_buf.head = *count;       /* Used as the buffer size. */
+
+                               /* Try to preload the whole FIFO. */
+                               preload_count = ME4600_AO_FIFO_COUNT;
+
+                               if (preload_count > instance->wrap_count)
+                                       preload_count = instance->wrap_count;
+
+                               /* Preload the FIFO. */
+                               for (i = 0; i < preload_count;
+                                    i++, instance->circ_buf.tail++) {
+                                       if (instance->circ_buf.tail >=
+                                           instance->circ_buf.head)
+                                               instance->circ_buf.tail = 0;
+
+                                       if (instance->ao_idx & 0x1)
+                                               outl(instance->circ_buf.
+                                                    buf[instance->circ_buf.
+                                                        tail] << 16,
+                                                    instance->fifo_reg);
+                                       else
+                                               outl(instance->circ_buf.
+                                                    buf[instance->circ_buf.
+                                                        tail],
+                                                    instance->fifo_reg);
+                               }
+
+                               instance->wrap_remaining =
+                                   instance->wrap_count - preload_count;
+                       } else {
+                               PERROR("To many values written.\n");
+                               err = ME_ERRNO_INVALID_VALUE_COUNT;
+                               goto ERROR;
+                       }
+               }
+
+               break;
+
+       case 2:         // Continuous mode
+               /* Check if in SW wrapround mode */
+               if (instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) {
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+                       PERROR("Subdevice is configured SW wrapround mode.\n");
+                       err = ME_ERRNO_PREVIOUS_CONFIG;
+                       goto ERROR;
+               }
+
+               switch (write_mode) {
+
+               case ME_WRITE_MODE_BLOCKING:
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       PDEBUG("Write for blocking continuous mode.\n");
+
+                       while (cnt > 0) {
+                               wait_event_interruptible(instance->wait_queue,
+                                                        (c =
+                                                         me_circ_buf_space_to_end
+                                                         (&instance->
+                                                          circ_buf)));
+
+                               if (instance->
+                                   flags & ME4600_AO_FLAGS_BROKEN_PIPE) {
+                                       PERROR
+                                           ("Broken pipe in blocking write.\n");
+                                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+                                       goto ERROR;
+                               } else if (signal_pending(current)) {
+                                       PERROR
+                                           ("Wait for free buffer interrupted from signal.\n");
+                                       err = ME_ERRNO_SIGNAL;
+                                       goto ERROR;
+                               }
+
+                               PDEBUG("Space to end = %d.\n", c);
+
+                               /* Only able to write size of free buffer or size of count */
+
+                               if (cnt < c)
+                                       c = cnt;
+                               k = sizeof(int) * c;
+                               k -= copy_from_user(instance->circ_buf.buf +
+                                                   instance->circ_buf.head,
+                                                   values, k);
+                               c = k / sizeof(int);
+
+                               PDEBUG("Copy %d values from user space.\n", c);
+
+                               if (!c) {
+                                       PERROR
+                                           ("Cannot copy values from user space.\n");
+                                       err = ME_ERRNO_INTERNAL;
+                                       goto ERROR;
+                               }
+
+                               instance->circ_buf.head =
+                                   (instance->circ_buf.head +
+                                    c) & (instance->circ_buf.mask);
+
+                               values += c;
+                               cnt -= c;
+                               ret += c;
+
+                               /* Values are now available so enable interrupts */
+                               spin_lock_irqsave(&instance->subdevice_lock,
+                                                 cpu_flags);
+
+                               if (me_circ_buf_space(&instance->circ_buf)) {
+                                       tmp = inl(instance->ctrl_reg);
+                                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                                       outl(tmp, instance->ctrl_reg);
+                               }
+
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                       }
+
+                       *count = ret;
+
+                       break;
+
+               case ME_WRITE_MODE_NONBLOCKING:
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       PDEBUG("Write for non blocking continuous mode.\n");
+
+                       while (cnt > 0) {
+                               if (instance->
+                                   flags & ME4600_AO_FLAGS_BROKEN_PIPE) {
+                                       PERROR
+                                           ("ME4600:Broken pipe in nonblocking write.\n");
+                                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+                                       goto ERROR;
+                               }
+
+                               c = me_circ_buf_space_to_end(&instance->
+                                                            circ_buf);
+
+                               if (!c) {
+                                       PDEBUG
+                                           ("Returning from nonblocking write.\n");
+                                       break;
+                               }
+
+                               PDEBUG("Space to end = %d.\n", c);
+
+                               /* Only able to write size of free buffer or size of count */
+
+                               if (cnt < c)
+                                       c = cnt;
+                               k = sizeof(int) * c;
+                               k -= copy_from_user(instance->circ_buf.buf +
+                                                   instance->circ_buf.head,
+                                                   values, k);
+                               c = k / sizeof(int);
+
+                               PDEBUG("Copy %d values from user space.\n", c);
+
+                               if (!c) {
+                                       PERROR
+                                           ("Cannot copy values from user space.\n");
+                                       err = ME_ERRNO_INTERNAL;
+                                       goto ERROR;
+                               }
+
+                               instance->circ_buf.head =
+                                   (instance->circ_buf.head +
+                                    c) & (instance->circ_buf.mask);
+
+                               values += c;
+                               cnt -= c;
+                               ret += c;
+
+                               /* Values are now available so enable interrupts */
+                               spin_lock_irqsave(&instance->subdevice_lock,
+                                                 cpu_flags);
+
+                               if (me_circ_buf_space(&instance->circ_buf)) {
+                                       tmp = inl(instance->ctrl_reg);
+                                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                                       outl(tmp, instance->ctrl_reg);
+                               }
+
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                       }
+
+                       *count = ret;
+
+                       break;
+
+               case ME_WRITE_MODE_PRELOAD:
+                       PDEBUG("Write for preload continuous mode.\n");
+
+                       if ((inl(instance->status_reg) &
+                            ME4600_AO_STATUS_BIT_FSM)) {
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               PERROR
+                                   ("Can't Preload DAC FIFO while conversion is running.\n");
+                               err = ME_ERRNO_SUBDEVICE_BUSY;
+                               goto ERROR;
+                       }
+
+                       tmp = inl(instance->ctrl_reg);
+
+                       tmp |=
+                           ME4600_AO_CTRL_BIT_STOP |
+                           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
+                       outl(tmp, instance->ctrl_reg);
+                       tmp &=
+                           ~(ME4600_AO_CTRL_BIT_ENABLE_FIFO |
+                             ME4600_AO_CTRL_BIT_ENABLE_IRQ);
+                       outl(tmp, instance->ctrl_reg);
+                       tmp |= ME4600_AO_CTRL_BIT_ENABLE_FIFO;
+                       outl(tmp, instance->ctrl_reg);
+
+                       instance->circ_buf.head = 0;
+                       instance->circ_buf.tail = 0;
+                       instance->flags &= ~ME4600_AO_FLAGS_BROKEN_PIPE;
+
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       c = ME4600_AO_FIFO_COUNT;
+
+                       if (cnt < c)
+                               c = cnt;
+
+                       for (i = 0; i < c; i++) {
+                               if (get_user(value, values)) {
+                                       PERROR
+                                           ("Can't copy value from user space.\n");
+                                       err = ME_ERRNO_INTERNAL;
+                                       goto ERROR;
+                               }
+
+                               if (instance->ao_idx & 0x1)
+                                       value <<= 16;
+
+                               outl(value, instance->fifo_reg);
+
+                               values++;
+                       }
+
+                       cnt -= c;
+
+                       ret += c;
+
+                       PDEBUG("Wrote %d values to fifo.\n", c);
+
+                       while (1) {
+                               c = me_circ_buf_space_to_end(&instance->
+                                                            circ_buf);
+
+                               if (c == 0)
+                                       break;
+
+                               if (cnt < c)
+                                       c = cnt;
+
+                               if (c <= 0)
+                                       break;
+
+                               k = sizeof(int) * c;
+
+                               k -= copy_from_user(instance->circ_buf.buf +
+                                                   instance->circ_buf.head,
+                                                   values, k);
+
+                               c = k / sizeof(int);
+
+                               PDEBUG("Wrote %d values to circular buffer.\n",
+                                      c);
+
+                               if (!c) {
+                                       PERROR
+                                           ("Can't copy values from user space.\n");
+                                       err = ME_ERRNO_INTERNAL;
+                                       goto ERROR;
+                               }
+
+                               instance->circ_buf.head =
+                                   (instance->circ_buf.head +
+                                    c) & (instance->circ_buf.mask);
+
+                               values += c;
+                               cnt -= c;
+                               ret += c;
+                       }
+
+                       *count = ret;
+
+                       break;
+
+               default:
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       PERROR("Invalid write mode specified.\n");
+
+                       err = ME_ERRNO_INVALID_WRITE_MODE;
+
+                       goto ERROR;
+               }
+
+               break;
+
+       default:                // Single mode of invalid
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+               PERROR("Subdevice is configured in single mode.\n");
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+               goto ERROR;
+       }
+
+      ERROR:
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me4600_ao_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me4600_ao_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       unsigned long tmp;
+       int value;
+       me4600_ao_subdevice_t *instance = dev_id;
+       int i;
+       int c = 0;
+       int c1 = 0;
+
+       if (irq != instance->irq) {
+               PDEBUG("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       if (!((0x1 << (instance->ao_idx + 3)) & inl(instance->irq_status_reg))) {
+               return IRQ_NONE;
+       }
+
+       PDEBUG("executed.\n");
+
+       tmp = inl(instance->status_reg);
+
+       if (!(tmp & ME4600_AO_STATUS_BIT_EF) &&
+           (tmp & ME4600_AO_STATUS_BIT_HF) &&
+           (tmp & ME4600_AO_STATUS_BIT_HF)) {
+               c = ME4600_AO_FIFO_COUNT;
+               PDEBUG("Fifo empty.\n");
+       } else if ((tmp & ME4600_AO_STATUS_BIT_EF) &&
+                  (tmp & ME4600_AO_STATUS_BIT_HF) &&
+                  (tmp & ME4600_AO_STATUS_BIT_HF)) {
+               c = ME4600_AO_FIFO_COUNT / 2;
+               PDEBUG("Fifo under half full.\n");
+       } else {
+               c = 0;
+               PDEBUG("Fifo full.\n");
+       }
+
+       PDEBUG("Try to write 0x%04X values.\n", c);
+
+       if ((instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
+           ME4600_AO_FLAGS_SW_WRAP_MODE_INF) {
+               while (c) {
+                       c1 = c;
+
+                       if (c1 > (instance->circ_buf.head - instance->circ_buf.tail))   /* Only up to the end of the buffer */
+                               c1 = (instance->circ_buf.head -
+                                     instance->circ_buf.tail);
+
+                       /* Write the values to the FIFO */
+                       for (i = 0; i < c1; i++, instance->circ_buf.tail++, c--) {
+                               if (instance->ao_idx & 0x1)
+                                       outl(instance->circ_buf.
+                                            buf[instance->circ_buf.tail] << 16,
+                                            instance->fifo_reg);
+                               else
+                                       outl(instance->circ_buf.
+                                            buf[instance->circ_buf.tail],
+                                            instance->fifo_reg);
+                       }
+
+                       if (instance->circ_buf.tail >= instance->circ_buf.head) /* Start from beginning */
+                               instance->circ_buf.tail = 0;
+               }
+
+               spin_lock(&instance->subdevice_lock);
+
+               tmp = inl(instance->ctrl_reg);
+               tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(tmp, instance->ctrl_reg);
+               tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(tmp, instance->ctrl_reg);
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
+                       PERROR("Broken pipe.\n");
+                       instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE;
+                       tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(tmp, instance->ctrl_reg);
+               }
+
+               spin_unlock(&instance->subdevice_lock);
+       } else if ((instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
+                  ME4600_AO_FLAGS_SW_WRAP_MODE_FIN) {
+               while (c && instance->wrap_remaining) {
+                       c1 = c;
+
+                       if (c1 > (instance->circ_buf.head - instance->circ_buf.tail))   /* Only up to the end of the buffer */
+                               c1 = (instance->circ_buf.head -
+                                     instance->circ_buf.tail);
+
+                       if (c1 > instance->wrap_remaining)      /* Only up to count of user defined number of values */
+                               c1 = instance->wrap_remaining;
+
+                       /* Write the values to the FIFO */
+                       for (i = 0; i < c1;
+                            i++, instance->circ_buf.tail++, c--,
+                            instance->wrap_remaining--) {
+                               if (instance->ao_idx & 0x1)
+                                       outl(instance->circ_buf.
+                                            buf[instance->circ_buf.tail] << 16,
+                                            instance->fifo_reg);
+                               else
+                                       outl(instance->circ_buf.
+                                            buf[instance->circ_buf.tail],
+                                            instance->fifo_reg);
+                       }
+
+                       if (instance->circ_buf.tail >= instance->circ_buf.head) /* Start from beginning */
+                               instance->circ_buf.tail = 0;
+               }
+
+               spin_lock(&instance->subdevice_lock);
+
+               tmp = inl(instance->ctrl_reg);
+
+               if (!instance->wrap_remaining) {
+                       PDEBUG("Finite SW wraparound done.\n");
+                       tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+               }
+
+               tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+
+               outl(tmp, instance->ctrl_reg);
+               tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(tmp, instance->ctrl_reg);
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
+                       PERROR("Broken pipe.\n");
+                       instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE;
+                       tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(tmp, instance->ctrl_reg);
+               }
+
+               spin_unlock(&instance->subdevice_lock);
+
+       } else {                /* Regular continuous mode */
+
+               while (1) {
+                       c1 = me_circ_buf_values_to_end(&instance->circ_buf);
+                       PDEBUG("Values to end = %d.\n", c1);
+
+                       if (c1 > c)
+                               c1 = c;
+
+                       if (c1 <= 0) {
+                               PDEBUG("Work done or buffer empty.\n");
+                               break;
+                       }
+
+                       if (instance->ao_idx & 0x1) {
+                               for (i = 0; i < c1; i++) {
+                                       value =
+                                           *(instance->circ_buf.buf +
+                                             instance->circ_buf.tail +
+                                             i) << 16;
+                                       outl(value, instance->fifo_reg);
+                               }
+                       } else
+                               outsl(instance->fifo_reg,
+                                     instance->circ_buf.buf +
+                                     instance->circ_buf.tail, c1);
+
+                       instance->circ_buf.tail =
+                           (instance->circ_buf.tail +
+                            c1) & (instance->circ_buf.mask);
+
+                       PDEBUG("%d values wrote to port 0x%04X.\n", c1,
+                              instance->fifo_reg);
+
+                       c -= c1;
+               }
+
+               spin_lock(&instance->subdevice_lock);
+
+               tmp = inl(instance->ctrl_reg);
+
+               if (!me_circ_buf_values(&instance->circ_buf)) {
+                       PDEBUG
+                           ("Disable Interrupt because no values left in buffer.\n");
+                       tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+               }
+
+               tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ;
+
+               outl(tmp, instance->ctrl_reg);
+               tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
+               outl(tmp, instance->ctrl_reg);
+
+               if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
+                       PDEBUG("Broken pipe in me4600_ao_isr.\n");
+                       instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE;
+                       tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(tmp, instance->ctrl_reg);
+               }
+
+               spin_unlock(&instance->subdevice_lock);
+
+               wake_up_interruptible(&instance->wait_queue);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void me4600_ao_destructor(struct me_subdevice *subdevice)
+{
+       me4600_ao_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       free_irq(instance->irq, instance);
+       kfree(instance->circ_buf.buf);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
+                                            spinlock_t * preload_reg_lock,
+                                            uint32_t * preload_flags,
+                                            int ao_idx, int fifo, int irq)
+{
+       me4600_ao_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me4600_ao_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_ao_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->preload_reg_lock = preload_reg_lock;
+       subdevice->preload_flags = preload_flags;
+
+       /* Allocate and initialize circular buffer */
+       subdevice->circ_buf.mask = ME4600_AO_CIRC_BUF_COUNT - 1;
+       subdevice->circ_buf.buf = kmalloc(ME4600_AO_CIRC_BUF_SIZE, GFP_KERNEL);
+
+       if (!subdevice->circ_buf.buf) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               me_subdevice_deinit((me_subdevice_t *) subdevice);
+               kfree(subdevice);
+               return NULL;
+       }
+
+       memset(subdevice->circ_buf.buf, 0, ME4600_AO_CIRC_BUF_SIZE);
+
+       subdevice->circ_buf.head = 0;
+       subdevice->circ_buf.tail = 0;
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       /* Initialize single value to 0V */
+       subdevice->single_value = 0x8000;
+
+       /* Store analog output index */
+       subdevice->ao_idx = ao_idx;
+
+       /* Store if analog output has fifo */
+       subdevice->fifo = fifo;
+
+       /* Initialize registers */
+
+       if (ao_idx == 0) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_00_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_00_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_00_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_00_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_00_TIMER_REG;
+               subdevice->reg_base = reg_base;
+
+               if (inl(subdevice->reg_base + ME4600_AO_BOSCH_REG) == 0x20000) {
+                       PINFO("Bosch firmware in use for channel 0.\n");
+                       subdevice->bosch_fw = 1;
+               } else {
+                       subdevice->bosch_fw = 0;
+               }
+       } else if (ao_idx == 1) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_01_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_01_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_01_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_01_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_01_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bosch_fw = 0;
+       } else if (ao_idx == 2) {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_02_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_02_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_02_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_02_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_02_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bosch_fw = 0;
+       } else {
+               subdevice->ctrl_reg = reg_base + ME4600_AO_03_CTRL_REG;
+               subdevice->status_reg = reg_base + ME4600_AO_03_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME4600_AO_03_FIFO_REG;
+               subdevice->single_reg = reg_base + ME4600_AO_03_SINGLE_REG;
+               subdevice->timer_reg = reg_base + ME4600_AO_03_TIMER_REG;
+               subdevice->reg_base = reg_base;
+               subdevice->bosch_fw = 0;
+       }
+
+       subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
+       subdevice->preload_reg = reg_base + ME4600_AO_LOADSETREG_XX;
+
+       /* Register interrupt service routine */
+       subdevice->irq = irq;
+
+       if (request_irq
+           (subdevice->irq, me4600_ao_isr, SA_INTERRUPT | SA_SHIRQ,
+            ME4600_NAME, subdevice)) {
+               PERROR("Cannot get interrupt line.\n");
+               me_subdevice_deinit((me_subdevice_t *) subdevice);
+               kfree(subdevice->circ_buf.buf);
+               kfree(subdevice);
+               return NULL;
+       }
+
+       /* Override base class methods. */
+       subdevice->base.me_subdevice_destructor = me4600_ao_destructor;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_ao_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me4600_ao_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me4600_ao_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me4600_ao_io_single_write;
+       subdevice->base.me_subdevice_io_stream_config =
+           me4600_ao_io_stream_config;
+       subdevice->base.me_subdevice_io_stream_new_values =
+           me4600_ao_io_stream_new_values;
+       subdevice->base.me_subdevice_io_stream_write =
+           me4600_ao_io_stream_write;
+       subdevice->base.me_subdevice_io_stream_start =
+           me4600_ao_io_stream_start;
+       subdevice->base.me_subdevice_io_stream_status =
+           me4600_ao_io_stream_status;
+       subdevice->base.me_subdevice_io_stream_stop = me4600_ao_io_stream_stop;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_ao_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_ao_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_ao_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_subdevice_caps_args =
+           me4600_ao_query_subdevice_caps_args;
+       subdevice->base.me_subdevice_query_range_by_min_max =
+           me4600_ao_query_range_by_min_max;
+       subdevice->base.me_subdevice_query_number_ranges =
+           me4600_ao_query_number_ranges;
+       subdevice->base.me_subdevice_query_range_info =
+           me4600_ao_query_range_info;
+       subdevice->base.me_subdevice_query_timer = me4600_ao_query_timer;
+
+       return subdevice;
+}
+
+#endif // BOSCH
+
+/* Common functions
+*/
+
+static int me4600_ao_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range)
+{
+       me4600_ao_subdevice_t *instance;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if ((*max - *min) < 0) {
+               PERROR("Invalid minimum and maximum values specified.\n");
+               return ME_ERRNO_INVALID_MIN_MAX;
+       }
+
+       if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
+               if ((*max <= (ME4600_AO_MAX_RANGE + 1000))
+                   && (*min >= ME4600_AO_MIN_RANGE)) {
+                       *min = ME4600_AO_MIN_RANGE;
+                       *max = ME4600_AO_MAX_RANGE;
+                       *maxdata = ME4600_AO_MAX_DATA;
+                       *range = 0;
+               } else {
+                       PERROR("No matching range available.\n");
+                       return ME_ERRNO_NO_RANGE;
+               }
+       } else {
+               PERROR("Invalid physical unit specified.\n");
+               return ME_ERRNO_INVALID_UNIT;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count)
+{
+       me4600_ao_subdevice_t *instance;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
+               *count = 1;
+       } else {
+               *count = 0;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata)
+{
+       me4600_ao_subdevice_t *instance;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (range == 0) {
+               *unit = ME_UNIT_VOLT;
+               *min = ME4600_AO_MIN_RANGE;
+               *max = ME4600_AO_MAX_RANGE;
+               *maxdata = ME4600_AO_MAX_DATA;
+       } else {
+               PERROR("Invalid range number specified.\n");
+               return ME_ERRNO_INVALID_RANGE;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_timer(me_subdevice_t * subdevice,
+                                int timer,
+                                int *base_frequency,
+                                long long *min_ticks, long long *max_ticks)
+{
+       me4600_ao_subdevice_t *instance;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if ((timer != ME_TIMER_ACQ_START) && (timer != ME_TIMER_CONV_START)) {
+               PERROR("Invalid timer specified.\n");
+               return ME_ERRNO_INVALID_TIMER;
+       }
+
+       if (instance->fifo) {   //Streaming device.
+               *base_frequency = ME4600_AO_BASE_FREQUENCY;
+               if (timer == ME_TIMER_ACQ_START) {
+                       *min_ticks = ME4600_AO_MIN_ACQ_TICKS;
+                       *max_ticks = ME4600_AO_MAX_ACQ_TICKS;
+               } else if (timer == ME_TIMER_CONV_START) {
+                       *min_ticks = ME4600_AO_MIN_CHAN_TICKS;
+                       *max_ticks = ME4600_AO_MAX_CHAN_TICKS;
+               }
+       } else {                //Not streaming device!
+               *base_frequency = 0;
+               *min_ticks = 0;
+               *max_ticks = 0;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       me4600_ao_subdevice_t *instance;
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *number = 1;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       me4600_ao_subdevice_t *instance;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *type = ME_TYPE_AO;
+       *subtype = (instance->fifo) ? ME_SUBTYPE_STREAMING : ME_SUBTYPE_SINGLE;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       me4600_ao_subdevice_t *instance;
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *caps =
+           ME_CAPS_AO_TRIG_SYNCHRONOUS | ((instance->fifo) ? ME_CAPS_AO_FIFO :
+                                          ME_CAPS_NONE);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ao_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                              int cap, int *args, int count)
+{
+       me4600_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       instance = (me4600_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (count != 1) {
+               PERROR("Invalid capability argument count.\n");
+               return ME_ERRNO_INVALID_CAP_ARG_COUNT;
+       }
+
+       switch (cap) {
+       case ME_CAP_AI_FIFO_SIZE:
+               args[0] = (instance->fifo) ? ME4600_AO_FIFO_COUNT : 0;
+               break;
+
+       case ME_CAP_AI_BUFFER_SIZE:
+               args[0] =
+                   (instance->circ_buf.buf) ? ME4600_AO_CIRC_BUF_COUNT : 0;
+               break;
+
+       default:
+               PERROR("Invalid capability.\n");
+               err = ME_ERRNO_INVALID_CAP;
+               args[0] = 0;
+       }
+
+       return err;
+}
diff --git a/drivers/staging/meilhaus/me4600_ao.h b/drivers/staging/meilhaus/me4600_ao.h
new file mode 100644 (file)
index 0000000..6fbc4a2
--- /dev/null
@@ -0,0 +1,263 @@
+/**
+ * @file me4600_ao.h
+ *
+ * @brief Meilhaus ME-4000 analog output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_AO_H_
+# define _ME4600_AO_H_
+
+# include <linux/version.h>
+# include "mesubdevice.h"
+# include "mecirc_buf.h"
+# include "meioctl.h"
+
+# ifdef __KERNEL__
+
+#  ifdef BOSCH
+#   undef ME_SYNAPSE
+#   ifndef _CBUFF_32b_t
+#       define _CBUFF_32b_t
+#   endif //_CBUFF_32b_t
+#  endif //BOSCH
+
+#  define ME4600_AO_MAX_SUBDEVICES             4
+#  define ME4600_AO_FIFO_COUNT                 4096
+
+#  define ME4600_AO_BASE_FREQUENCY             33000000LL
+
+#  define ME4600_AO_MIN_ACQ_TICKS              0LL
+#  define ME4600_AO_MAX_ACQ_TICKS              0LL
+
+#  define ME4600_AO_MIN_CHAN_TICKS             66LL
+#  define ME4600_AO_MAX_CHAN_TICKS             0xFFFFFFFFLL
+
+#  define ME4600_AO_MIN_RANGE                  -10000000
+#  define ME4600_AO_MAX_RANGE                  9999694
+
+#  define ME4600_AO_MAX_DATA                   0xFFFF
+
+#  ifdef ME_SYNAPSE
+#   define ME4600_AO_CIRC_BUF_SIZE_ORDER               8       // 2^n PAGES =>> Maximum value of 1MB for Synapse
+#  else
+#   define ME4600_AO_CIRC_BUF_SIZE_ORDER               5       // 2^n PAGES =>> 128KB
+#  endif
+#  define ME4600_AO_CIRC_BUF_SIZE              PAGE_SIZE<<ME4600_AO_CIRC_BUF_SIZE_ORDER        // Buffer size in bytes.
+
+#  ifdef _CBUFF_32b_t
+#   define ME4600_AO_CIRC_BUF_COUNT    ((ME4600_AO_CIRC_BUF_SIZE) / sizeof(uint32_t))  // Size in values
+#  else
+#   define ME4600_AO_CIRC_BUF_COUNT    ((ME4600_AO_CIRC_BUF_SIZE) / sizeof(uint16_t))  // Size in values
+#  endif
+
+#  define ME4600_AO_CONTINOUS                                  0x0
+#  define ME4600_AO_WRAP_MODE                                  0x1
+#  define ME4600_AO_HW_MODE                                            0x2
+
+#  define ME4600_AO_HW_WRAP_MODE                               (ME4600_AO_WRAP_MODE | ME4600_AO_HW_MODE)
+#  define ME4600_AO_SW_WRAP_MODE                               ME4600_AO_WRAP_MODE
+
+#  define ME4600_AO_INF_STOP_MODE                              0x0
+#  define ME4600_AO_ACQ_STOP_MODE                              0x1
+#  define ME4600_AO_SCAN_STOP_MODE                             0x2
+
+#  ifdef BOSCH                 //SPECIAL BUILD FOR BOSCH
+
+/* Bits for flags attribute. */
+#   define ME4600_AO_FLAGS_BROKEN_PIPE                 0x1
+#   define ME4600_AO_FLAGS_SW_WRAP_MODE_0              0x2
+#   define ME4600_AO_FLAGS_SW_WRAP_MODE_1              0x4
+#   define ME4600_AO_FLAGS_SW_WRAP_MODE_MASK   (ME4600_AO_FLAGS_SW_WRAP_MODE_0 | ME4600_AO_FLAGS_SW_WRAP_MODE_1)
+
+#   define ME4600_AO_FLAGS_SW_WRAP_MODE_NONE   0x0
+#   define ME4600_AO_FLAGS_SW_WRAP_MODE_INF            0x2
+#   define ME4600_AO_FLAGS_SW_WRAP_MODE_FIN            0x4
+
+       /**
+       * @brief The ME-4000 analog output subdevice class.
+       */
+typedef struct me4600_ao_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                            /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;                      /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *preload_reg_lock;           /**< Spin lock to protect #preload_reg from concurrent access. */
+       uint32_t *preload_flags;
+
+       unsigned int irq;                                       /**< The interrupt request number assigned by the PCI BIOS. */
+       me_circ_buf_t circ_buf;                         /**< Circular buffer holding measurment data. */
+       wait_queue_head_t wait_queue;           /**< Wait queue to put on tasks waiting for data to arrive. */
+
+       int single_value;                                       /**< Mirror of the value written in single mode. */
+
+       int volatile flags;                                     /**< Flags used for storing SW wraparound setup and error signalling from ISR. */
+       unsigned int wrap_count;                        /**< The user defined wraparound cycle count. */
+       unsigned int wrap_remaining;            /**< The wraparound cycle down counter used by a running conversion. */
+       unsigned int ao_idx;                            /**< The index of this analog output on this device. */
+       int fifo;                                                       /**< If set this device has a FIFO. */
+
+       int bosch_fw;                                           /**< If set the bosch firmware is in PROM. */
+
+       /* Registers */
+       uint32_t ctrl_reg;
+       uint32_t status_reg;
+       uint32_t fifo_reg;
+       uint32_t single_reg;
+       uint32_t timer_reg;
+       uint32_t irq_status_reg;
+       uint32_t preload_reg;
+       uint32_t reg_base;
+} me4600_ao_subdevice_t;
+
+       /**
+       * @brief The constructor to generate a ME-4000 analog output subdevice instance for BOSCH project.
+       *
+       * @param reg_base The register base address of the device as returned by the PCI BIOS.
+       * @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
+       * @param preload_flags Pointer to spin lock protecting the hold&trigger register from concurrent access.
+       * @param ao_idx Subdevice number.
+       * @param fifo Flag set if subdevice has hardware FIFO.
+       * @param irq IRQ number.
+       *
+       * @return Pointer to new instance on success.\n
+       * NULL on error.
+       */
+me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
+                                            spinlock_t * preload_reg_lock,
+                                            uint32_t * preload_flags,
+                                            int ao_idx, int fifo, int irq);
+
+#  else        //~BOSCH
+
+//ME4600_AO_FLAGS_BROKEN_PIPE is OBSOLETE => Now problems are reported in status.
+
+typedef enum ME4600_AO_STATUS {
+       ao_status_none = 0,
+       ao_status_single_configured,
+       ao_status_single_run_wait,
+       ao_status_single_run,
+       ao_status_single_end_wait,
+       ao_status_single_end,
+       ao_status_stream_configured,
+       ao_status_stream_run_wait,
+       ao_status_stream_run,
+       ao_status_stream_end_wait,
+       ao_status_stream_end,
+       ao_status_stream_fifo_error,
+       ao_status_stream_buffer_error,
+       ao_status_stream_error,
+       ao_status_last
+} ME4600_AO_STATUS;
+
+typedef struct me4600_ao_timeout {
+       unsigned long start_time;
+       unsigned long delay;
+} me4600_ao_timeout_t;
+
+       /**
+       * @brief The ME-4600 analog output subdevice class.
+       */
+typedef struct me4600_ao_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                                            /**< The subdevice base class. */
+       unsigned int ao_idx;                                            /**< The index of this analog output on this device. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;                                      /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *preload_reg_lock;                           /**< Spin lock to protect preload_reg from concurrent access. */
+
+       uint32_t *preload_flags;
+
+       /* Hardware feautres */
+       unsigned int irq;                                                       /**< The interrupt request number assigned by the PCI BIOS. */
+       int fifo;                                                                       /**< If set this device has a FIFO. */
+       int bitpattern;                                                         /**< If set this device use bitpattern. */
+
+       int single_value;                                                       /**< Mirror of the output value in single mode. */
+       int single_value_in_fifo;                                       /**< Mirror of the value written in single mode. */
+       uint32_t ctrl_trg;                                                      /**< Mirror of the trigger settings. */
+
+       volatile int mode;                                                      /**< Flags used for storing SW wraparound setup*/
+       int stop_mode;                                                          /**< The user defined stop condition flag. */
+       unsigned int start_mode;
+       unsigned int stop_count;                                        /**< The user defined dates presentation end count. */
+       unsigned int stop_data_count;                           /**< The stop presentation count. */
+       unsigned int data_count;                                        /**< The real presentation count. */
+       unsigned int preloaded_count;                           /**< The next data addres in buffer. <= for wraparound mode. */
+       int hardware_stop_delay;                                        /**< The time that stop can take. This is only to not show hardware bug to user. */
+
+       volatile enum ME4600_AO_STATUS status;          /**< The current stream status flag. */
+       me4600_ao_timeout_t timeout;                            /**< The timeout for start in blocking and non-blocking mode. */
+
+                                                                               /* Registers *//**< All registers are 32 bits long. */
+       unsigned long ctrl_reg;
+       unsigned long status_reg;
+       unsigned long fifo_reg;
+       unsigned long single_reg;
+       unsigned long timer_reg;
+       unsigned long irq_status_reg;
+       unsigned long preload_reg;
+       unsigned long reg_base;
+
+       /* Software buffer */
+       me_circ_buf_t circ_buf;                                         /**< Circular buffer holding measurment data. 32 bit long */
+       wait_queue_head_t wait_queue;                           /**< Wait queue to put on tasks waiting for data to arrive. */
+
+       struct workqueue_struct *me4600_workqueue;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       struct work_struct ao_control_task;
+#else
+       struct delayed_work ao_control_task;
+#endif
+
+       volatile int ao_control_task_flag;                      /**< Flag controling reexecuting of control task */
+
+} me4600_ao_subdevice_t;
+
+       /**
+       * @brief The constructor to generate a ME-4600 analog output subdevice instance.
+       *
+       * @param reg_base The register base address of the device as returned by the PCI BIOS.
+       * @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
+       * @param preload_flags Pointer to spin lock protecting the hold&trigger register from concurrent access.
+       * @param ao_idx Subdevice number.
+       * @param fifo Flag set if subdevice has hardware FIFO.
+       * @param irq IRQ number.
+       * @param me4600_wq Queue for asynchronous task (1 queue for all subdevice on 1 board).
+       *
+       * @return Pointer to new instance on success.\n
+       * NULL on error.
+       */
+me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
+                                            spinlock_t * preload_reg_lock,
+                                            uint32_t * preload_flags,
+                                            int ao_idx,
+                                            int fifo,
+                                            int irq,
+                                            struct workqueue_struct
+                                            *me4600_wq);
+
+#  endif //BOSCH
+# endif        //__KERNEL__
+#endif // ~_ME4600_AO_H_
diff --git a/drivers/staging/meilhaus/me4600_ao_reg.h b/drivers/staging/meilhaus/me4600_ao_reg.h
new file mode 100644 (file)
index 0000000..f83d82e
--- /dev/null
@@ -0,0 +1,113 @@
+/**
+ * @file me4600_ao_reg.h
+ *
+ * @brief ME-4000 analog output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_AO_REG_H_
+#define _ME4600_AO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME4600_AO_00_CTRL_REG                                  0x00    // R/W
+#define ME4600_AO_00_STATUS_REG                                        0x04    // R/_
+#define ME4600_AO_00_FIFO_REG                                  0x08    // _/W
+#define ME4600_AO_00_SINGLE_REG                                        0x0C    // R/W
+#define ME4600_AO_00_TIMER_REG                                 0x10    // _/W
+
+#define ME4600_AO_01_CTRL_REG                                  0x18    // R/W
+#define ME4600_AO_01_STATUS_REG                                        0x1C    // R/_
+#define ME4600_AO_01_FIFO_REG                                  0x20    // _/W
+#define ME4600_AO_01_SINGLE_REG                                        0x24    // R/W
+#define ME4600_AO_01_TIMER_REG                                 0x28    // _/W
+
+#define ME4600_AO_02_CTRL_REG                                  0x30    // R/W
+#define ME4600_AO_02_STATUS_REG                                        0x34    // R/_
+#define ME4600_AO_02_FIFO_REG                                  0x38    // _/W
+#define ME4600_AO_02_SINGLE_REG                                        0x3C    // R/W
+#define ME4600_AO_02_TIMER_REG                                 0x40    // _/W
+
+#define ME4600_AO_03_CTRL_REG                                  0x48    // R/W
+#define ME4600_AO_03_STATUS_REG                                        0x4C    // R/_
+#define ME4600_AO_03_FIFO_REG                                  0x50    // _/W
+#define ME4600_AO_03_SINGLE_REG                                        0x54    // R/W
+#define ME4600_AO_03_TIMER_REG                                 0x58    // _/W
+
+#define ME4600_AO_DEMUX_ADJUST_REG                             0xBC    // -/W
+#define ME4600_AO_DEMUX_ADJUST_VALUE                   0x4C
+
+#ifdef BOSCH
+# define ME4600_AO_BOSCH_REG                                   0xC4
+
+# define ME4600_AO_LOADSETREG_XX                               0xB4    // R/W
+
+# define ME4600_AO_CTRL_BIT_MODE_0                             0x001
+# define ME4600_AO_CTRL_BIT_MODE_1                             0x002
+# define ME4600_AO_CTRL_MASK_MODE                              0x003
+
+#else //~BOSCH
+
+#define ME4600_AO_SYNC_REG                                             0xB4    // R/W    ///ME4600_AO_SYNC_REG <==> ME4600_AO_PRELOAD_REG <==> ME4600_AO_LOADSETREG_XX
+
+# define ME4600_AO_MODE_SINGLE                                 0x00000000
+# define ME4600_AO_MODE_WRAPAROUND                             0x00000001
+# define ME4600_AO_MODE_CONTINUOUS                             0x00000002
+# define ME4600_AO_CTRL_MODE_MASK                              (ME4600_AO_MODE_WRAPAROUND | ME4600_AO_MODE_CONTINUOUS)
+#endif //BOSCH
+
+#define ME4600_AO_CTRL_BIT_MODE_WRAPAROUND             ME4600_AO_MODE_WRAPAROUND
+#define ME4600_AO_CTRL_BIT_MODE_CONTINOUS              ME4600_AO_MODE_CONTINUOUS
+#define ME4600_AO_CTRL_BIT_STOP                                        0x00000004
+#define ME4600_AO_CTRL_BIT_ENABLE_FIFO                 0x00000008
+#define ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG              0x00000010
+#define ME4600_AO_CTRL_BIT_EX_TRIG_EDGE                        0x00000020
+#define ME4600_AO_CTRL_BIT_IMMEDIATE_STOP              0x00000080
+#define ME4600_AO_CTRL_BIT_ENABLE_DO                   0x00000100
+#define ME4600_AO_CTRL_BIT_ENABLE_IRQ                  0x00000200
+#define ME4600_AO_CTRL_BIT_RESET_IRQ                   0x00000400
+#define ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH   0x00000800
+/*
+#define ME4600_AO_SYNC_HOLD_0                                  0x00000001
+#define ME4600_AO_SYNC_HOLD_1                                  0x00000002
+#define ME4600_AO_SYNC_HOLD_2                                  0x00000004
+#define ME4600_AO_SYNC_HOLD_3                                  0x00000008
+*/
+#define ME4600_AO_SYNC_HOLD                                            0x00000001
+
+/*
+#define ME4600_AO_SYNC_EXT_TRIG_0                              0x00010000
+#define ME4600_AO_SYNC_EXT_TRIG_1                              0x00020000
+#define ME4600_AO_SYNC_EXT_TRIG_2                              0x00040000
+#define ME4600_AO_SYNC_EXT_TRIG_3                              0x00080000
+*/
+#define ME4600_AO_SYNC_EXT_TRIG                                        0x00010000
+
+#define ME4600_AO_EXT_TRIG                                             0x80000000
+
+#define ME4600_AO_STATUS_BIT_FSM                               0x00000001
+#define ME4600_AO_STATUS_BIT_FF                                        0x00000002
+#define ME4600_AO_STATUS_BIT_HF                                        0x00000004
+#define ME4600_AO_STATUS_BIT_EF                                        0x00000008
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_device.c b/drivers/staging/meilhaus/me4600_device.c
new file mode 100644 (file)
index 0000000..fa45584
--- /dev/null
@@ -0,0 +1,373 @@
+/**
+ * @file me4600_device.c
+ *
+ * @brief ME-4600 device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "me4600_device.h"
+#include "meplx_reg.h"
+
+#include "mefirmware.h"
+
+#include "mesubdevice.h"
+#include "me4600_do.h"
+#include "me4600_di.h"
+#include "me4600_dio.h"
+#include "me8254.h"
+#include "me4600_ai.h"
+#include "me4600_ao.h"
+#include "me4600_ext_irq.h"
+
+/**
+ * @brief Global variable.
+ * This is working queue for runing a separate atask that will be responsible for work status (start, stop, timeouts).
+ */
+static struct workqueue_struct *me4600_workqueue;
+
+#ifdef BOSCH
+me_device_t *me4600_pci_constructor(struct pci_dev *pci_device, int me_bosch_fw)
+#else //~BOSCH
+me_device_t *me4600_pci_constructor(struct pci_dev *pci_device)
+#endif                         //BOSCH
+{
+       me4600_device_t *me4600_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me4600_device = kmalloc(sizeof(me4600_device_t), GFP_KERNEL);
+
+       if (!me4600_device) {
+               PERROR("Cannot get memory for ME-4600 device instance.\n");
+               return NULL;
+       }
+
+       memset(me4600_device, 0, sizeof(me4600_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me4600_device, pci_device);
+
+       if (err) {
+               kfree(me4600_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+       // Download the xilinx firmware.
+       if (me4600_device->base.info.pci.device_id == PCI_DEVICE_ID_MEILHAUS_ME4610) {  //Jekyll <=> me4610
+               err =
+                   me_xilinx_download(me4600_device->base.info.pci.
+                                      reg_bases[1],
+                                      me4600_device->base.info.pci.
+                                      reg_bases[5], &pci_device->dev,
+                                      "me4610.bin");
+       } else {                // General me4600 firmware
+#ifdef BOSCH
+               err =
+                   me_xilinx_download(me4600_device->base.info.pci.
+                                      reg_bases[1],
+                                      me4600_device->base.info.pci.
+                                      reg_bases[5], &pci_device->dev,
+                                      (me_bosch_fw) ? "me4600_bosch.bin" :
+                                      "me4600.bin");
+#else //~BOSCH
+               err =
+                   me_xilinx_download(me4600_device->base.info.pci.
+                                      reg_bases[1],
+                                      me4600_device->base.info.pci.
+                                      reg_bases[5], &pci_device->dev,
+                                      "me4600.bin");
+#endif
+       }
+
+       if (err) {
+               me_device_deinit((me_device_t *) me4600_device);
+               kfree(me4600_device);
+               PERROR("Cannot download firmware.\n");
+               return NULL;
+       }
+       // Get the index in the device version information table.
+       version_idx =
+           me4600_versions_get_device_index(me4600_device->base.info.pci.
+                                            device_id);
+
+       // Initialize spin locks.
+       spin_lock_init(&me4600_device->preload_reg_lock);
+
+       me4600_device->preload_flags = 0;
+
+       spin_lock_init(&me4600_device->dio_lock);
+       spin_lock_init(&me4600_device->ai_ctrl_lock);
+       spin_lock_init(&me4600_device->ctr_ctrl_reg_lock);
+       spin_lock_init(&me4600_device->ctr_clk_src_reg_lock);
+
+       // Create digital input instances.
+       for (i = 0; i < me4600_versions[version_idx].di_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me4600_di_constructor(me4600_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            &me4600_device->
+                                                            dio_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Create digital output instances.
+       for (i = 0; i < me4600_versions[version_idx].do_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me4600_do_constructor(me4600_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            &me4600_device->
+                                                            dio_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Create digital input/output instances.
+       for (i = 0; i < me4600_versions[version_idx].dio_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me4600_dio_constructor(me4600_device->
+                                                             base.info.pci.
+                                                             reg_bases[2],
+                                                             me4600_versions
+                                                             [version_idx].
+                                                             do_subdevices +
+                                                             me4600_versions
+                                                             [version_idx].
+                                                             di_subdevices + i,
+                                                             &me4600_device->
+                                                             dio_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Create analog input instances.
+       for (i = 0; i < me4600_versions[version_idx].ai_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me4600_ai_constructor(me4600_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            me4600_versions
+                                                            [version_idx].
+                                                            ai_channels,
+                                                            me4600_versions
+                                                            [version_idx].
+                                                            ai_ranges,
+                                                            me4600_versions
+                                                            [version_idx].
+                                                            ai_isolated,
+                                                            me4600_versions
+                                                            [version_idx].
+                                                            ai_sh,
+                                                            me4600_device->
+                                                            base.irq,
+                                                            &me4600_device->
+                                                            ai_ctrl_lock,
+                                                            me4600_workqueue);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Create analog output instances.
+       for (i = 0; i < me4600_versions[version_idx].ao_subdevices; i++) {
+#ifdef BOSCH
+               subdevice =
+                   (me_subdevice_t *) me4600_ao_constructor(me4600_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            &me4600_device->
+                                                            preload_reg_lock,
+                                                            &me4600_device->
+                                                            preload_flags, i,
+                                                            me4600_versions
+                                                            [version_idx].
+                                                            ao_fifo,
+                                                            me4600_device->
+                                                            base.irq);
+#else //~BOSCH
+               subdevice =
+                   (me_subdevice_t *) me4600_ao_constructor(me4600_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            &me4600_device->
+                                                            preload_reg_lock,
+                                                            &me4600_device->
+                                                            preload_flags, i,
+                                                            me4600_versions
+                                                            [version_idx].
+                                                            ao_fifo,
+                                                            me4600_device->
+                                                            base.irq,
+                                                            me4600_workqueue);
+#endif
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Create counter instances.
+       for (i = 0; i < me4600_versions[version_idx].ctr_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8254_constructor(me4600_device->base.
+                                                         info.pci.device_id,
+                                                         me4600_device->base.
+                                                         info.pci.reg_bases[3],
+                                                         0, i,
+                                                         &me4600_device->
+                                                         ctr_ctrl_reg_lock,
+                                                         &me4600_device->
+                                                         ctr_clk_src_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       // Create external interrupt instances.
+       for (i = 0; i < me4600_versions[version_idx].ext_irq_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *)
+                   me4600_ext_irq_constructor(me4600_device->base.info.pci.
+                                              reg_bases[2],
+                                              me4600_device->base.irq,
+                                              &me4600_device->ai_ctrl_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me4600_device);
+                       kfree(me4600_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me4600_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me4600_device;
+}
+
+// Init and exit of module.
+
+static int __init me4600_init(void)
+{
+       PDEBUG("executed.\n");
+
+#ifndef BOSCH
+       me4600_workqueue = create_singlethread_workqueue("me4600");
+#endif
+       return 0;
+}
+
+static void __exit me4600_exit(void)
+{
+       PDEBUG("executed.\n");
+
+#ifndef BOSCH
+       flush_workqueue(me4600_workqueue);
+       destroy_workqueue(me4600_workqueue);
+#endif
+}
+
+module_init(me4600_init);
+module_exit(me4600_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for ME-46xx Devices");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-46xx Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me4600_pci_constructor);
diff --git a/drivers/staging/meilhaus/me4600_device.h b/drivers/staging/meilhaus/me4600_device.h
new file mode 100644 (file)
index 0000000..fa812d4
--- /dev/null
@@ -0,0 +1,151 @@
+/**
+ * @file me4600_device.h
+ *
+ * @brief ME-4600 device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_DEVICE_H
+#define _ME4600_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding ME-4600 device capabilities.
+ */
+typedef struct me4600_version {
+       uint16_t device_id;
+       unsigned int do_subdevices;
+       unsigned int di_subdevices;
+       unsigned int dio_subdevices;
+       unsigned int ctr_subdevices;
+       unsigned int ai_subdevices;
+       unsigned int ai_channels;
+       unsigned int ai_ranges;
+       unsigned int ai_isolated;
+       unsigned int ai_sh;
+       unsigned int ao_subdevices;
+       unsigned int ao_fifo;   //How many devices have FIFO
+       unsigned int ext_irq_subdevices;
+} me4600_version_t;
+
+/**
+ * @brief ME-4600 device capabilities.
+ */
+static me4600_version_t me4600_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME4610, 0, 0, 4, 3, 1, 16, 1, 0, 0, 0, 0, 1},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME4650, 0, 0, 4, 0, 1, 16, 4, 0, 0, 0, 0, 1},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME4660, 0, 0, 4, 3, 1, 16, 4, 0, 0, 2, 0, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4660I, 1, 1, 2, 3, 1, 16, 4, 1, 0, 2, 0, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4660S, 0, 0, 4, 3, 1, 16, 4, 0, 1, 2, 0, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4660IS, 1, 1, 2, 3, 1, 16, 4, 1, 1, 2, 0, 1},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME4670, 0, 0, 4, 3, 1, 32, 4, 0, 0, 4, 0, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4670I, 1, 1, 2, 3, 1, 32, 4, 1, 0, 4, 0, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4670S, 0, 0, 4, 3, 1, 32, 4, 0, 1, 4, 0, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4670IS, 1, 1, 2, 3, 1, 32, 4, 1, 1, 4, 0, 1},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME4680, 0, 0, 4, 3, 1, 32, 4, 0, 0, 4, 4, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4680I, 1, 1, 2, 3, 1, 32, 4, 1, 0, 4, 4, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4680S, 0, 0, 4, 3, 1, 32, 4, 0, 1, 4, 4, 1},
+       {PCI_DEVICE_ID_MEILHAUS_ME4680IS, 1, 1, 2, 3, 1, 32, 4, 1, 1, 4, 4, 1},
+
+       {0},
+};
+
+#define ME4600_DEVICE_VERSIONS (sizeof(me4600_versions) / sizeof(me4600_version_t) - 1)        /**< Returns the number of entries in #me4600_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me4600_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me4600_versions.
+ */
+static inline unsigned int me4600_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME4600_DEVICE_VERSIONS; i++)
+               if (me4600_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-4600 device class structure.
+ */
+typedef struct me4600_device {
+       me_device_t base;                                       /**< The Meilhaus device base class. */
+
+       /* Child class attributes. */
+       spinlock_t preload_reg_lock;            /**< Guards the preload register of the anaolog output devices. */
+       unsigned int preload_flags;                     /**< Used in conjunction with #preload_reg_lock. */
+       spinlock_t dio_lock;                            /**< Locks the control register of the digital input/output subdevices. */
+       spinlock_t ai_ctrl_lock;                        /**< Locks the control register of the analog input subdevice. */
+       spinlock_t ctr_ctrl_reg_lock;           /**< Locks the counter control register. */
+       spinlock_t ctr_clk_src_reg_lock;        /**< Not used on this device but needed for the me8254 subdevice constructor call. */
+} me4600_device_t;
+
+/**
+ * @brief The ME-4600 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ * @param me_bosch_fw If set the device shall use the bosch firmware. (Only for special BOSCH build)
+ *
+ * @return On succes a new ME-4600 device instance. \n
+ *         NULL on error.
+ */
+
+#ifdef BOSCH
+/**
+ * @brief The ME-4600 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ * @param me_bosch_fw If set the device shall use the bosch firmware.
+ *
+ * @return On succes a new ME-4600 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me4600_pci_constructor(struct pci_dev *pci_device, int me_bosch_fw)
+    __attribute__ ((weak));
+#else //~BOSCH
+/**
+ * @brief The ME-4600 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-4600 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me4600_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+#endif
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_di.c b/drivers/staging/meilhaus/me4600_di.c
new file mode 100644 (file)
index 0000000..7e3c9f4
--- /dev/null
@@ -0,0 +1,256 @@
+/**
+ * @file me4600_di.c
+ *
+ * @brief ME-4000 digital input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me4600_dio_reg.h"
+#include "me4600_di.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me4600_di_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me4600_di_subdevice_t *instance;
+       uint32_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_di_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inl(instance->ctrl_reg);
+       mode &= ~(ME4600_DIO_CTRL_BIT_MODE_2 | ME4600_DIO_CTRL_BIT_MODE_3);     //0xFFF3
+       outl(mode, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, mode);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outl(0, instance->port_reg);
+       PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_di_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me4600_di_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                       } else {
+                               PERROR("Invalid port direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_di_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me4600_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inl(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inl(instance->port_reg) & 0xFF;
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_di_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_di_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DI;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me4600_di_subdevice_t *me4600_di_constructor(uint32_t reg_base,
+                                            spinlock_t * ctrl_reg_lock)
+{
+       me4600_di_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me4600_di_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_di_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the subdevice index */
+       subdevice->port_reg = reg_base + ME4600_DIO_PORT_1_REG;
+       subdevice->ctrl_reg = reg_base + ME4600_DIO_CTRL_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_di_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me4600_di_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me4600_di_io_single_read;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_di_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_di_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_di_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me4600_di.h b/drivers/staging/meilhaus/me4600_di.h
new file mode 100644 (file)
index 0000000..ec8b175
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * @file me4600_di.h
+ *
+ * @brief ME-4000 digital input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_DI_H_
+#define _ME4600_DI_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me4600_di_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Register to configure the port direction. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me4600_di_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-4000 digital input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me4600_di_subdevice_t *me4600_di_constructor(uint32_t reg_base,
+                                            spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_dio.c b/drivers/staging/meilhaus/me4600_dio.c
new file mode 100644 (file)
index 0000000..0af95d1
--- /dev/null
@@ -0,0 +1,510 @@
+/**
+ * @file me4600_dio.c
+ *
+ * @brief ME-4000 digital input/output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me4600_dio_reg.h"
+#include "me4600_dio.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me4600_dio_io_reset_subdevice(struct me_subdevice *subdevice,
+                                        struct file *filep, int flags)
+{
+       me4600_dio_subdevice_t *instance;
+       uint32_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_dio_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       /* Set port to input mode */
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inl(instance->ctrl_reg);
+       mode &=
+           ~((ME4600_DIO_CTRL_BIT_MODE_0 | ME4600_DIO_CTRL_BIT_MODE_1) <<
+             (instance->dio_idx * 2));
+       outl(mode, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, mode);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outl(0, instance->port_reg);
+       PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_dio_io_single_config(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int single_config,
+                                      int ref,
+                                      int trig_chan,
+                                      int trig_type, int trig_edge, int flags)
+{
+       me4600_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t mode;
+       uint32_t size =
+           flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
+                    | ME_IO_SINGLE_CONFIG_DIO_WORD |
+                    ME_IO_SINGLE_CONFIG_DIO_DWORD);
+       uint32_t mask;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inl(instance->ctrl_reg);
+       switch (size) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                               mode &=
+                                   ~((ME4600_DIO_CTRL_BIT_MODE_0 |
+                                      ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                     (instance->dio_idx * 2));
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               mode &=
+                                   ~((ME4600_DIO_CTRL_BIT_MODE_0 |
+                                      ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                     (instance->dio_idx * 2));
+                               mode |=
+                                   ME4600_DIO_CTRL_BIT_MODE_0 << (instance->
+                                                                  dio_idx * 2);
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_MUX32M) {
+                               mask =
+                                   (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                    ME4600_DIO_CTRL_BIT_MODE_1) << (instance->
+                                                                    dio_idx *
+                                                                    2);
+                               mask |=
+                                   ME4600_DIO_CTRL_BIT_FUNCTION_0 |
+                                   ME4600_DIO_CTRL_BIT_FUNCTION_1;
+                               mask |=
+                                   ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
+                                   instance->dio_idx;
+                               mode &= ~mask;
+
+                               if (ref == ME_REF_DIO_FIFO_LOW) {
+                                       mode |=
+                                           (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                            ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                           (instance->dio_idx * 2);
+                                       mode |= ME4600_DIO_CTRL_BIT_FUNCTION_1;
+                               } else if (ref == ME_REF_DIO_FIFO_HIGH) {
+                                       mode |=
+                                           (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                            ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                           (instance->dio_idx * 2);
+                                       mode |= ME4600_DIO_CTRL_BIT_FUNCTION_1;
+                                       mode |=
+                                           ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
+                                           instance->dio_idx;
+                               } else {
+                                       PERROR
+                                           ("Invalid port reference specified.\n");
+                                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                               }
+                       } else if (single_config ==
+                                  ME_SINGLE_CONFIG_DIO_DEMUX32) {
+                               mask =
+                                   (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                    ME4600_DIO_CTRL_BIT_MODE_1) << (instance->
+                                                                    dio_idx *
+                                                                    2);
+                               mask |=
+                                   ME4600_DIO_CTRL_BIT_FUNCTION_0 |
+                                   ME4600_DIO_CTRL_BIT_FUNCTION_1;
+                               mask |=
+                                   ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
+                                   instance->dio_idx;
+                               mode &= ~mask;
+
+                               if (ref == ME_REF_DIO_FIFO_LOW) {
+                                       mode |=
+                                           (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                            ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                           (instance->dio_idx * 2);
+                                       mode |= ME4600_DIO_CTRL_BIT_FUNCTION_0;
+                               } else if (ref == ME_REF_DIO_FIFO_HIGH) {
+                                       mode |=
+                                           (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                            ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                           (instance->dio_idx * 2);
+                                       mode |= ME4600_DIO_CTRL_BIT_FUNCTION_0;
+                                       mode |=
+                                           ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
+                                           instance->dio_idx;
+                               } else {
+                                       PERROR
+                                           ("Invalid port reference specified.\n");
+                                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                               }
+                       } else if (single_config ==
+                                  ME_SINGLE_CONFIG_DIO_BIT_PATTERN) {
+                               mask =
+                                   (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                    ME4600_DIO_CTRL_BIT_MODE_1) << (instance->
+                                                                    dio_idx *
+                                                                    2);
+                               mask |=
+                                   ME4600_DIO_CTRL_BIT_FUNCTION_0 |
+                                   ME4600_DIO_CTRL_BIT_FUNCTION_1;
+                               mask |=
+                                   ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
+                                   instance->dio_idx;
+                               mode &= ~mask;
+
+                               if (ref == ME_REF_DIO_FIFO_LOW) {
+                                       mode |=
+                                           (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                            ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                           (instance->dio_idx * 2);
+                               } else if (ref == ME_REF_DIO_FIFO_HIGH) {
+                                       mode |=
+                                           (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                            ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                           (instance->dio_idx * 2);
+                                       mode |=
+                                           ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
+                                           instance->dio_idx;
+                               } else {
+                                       PERROR
+                                           ("Invalid port reference specified.\n");
+                                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                               }
+                       } else {
+                               PERROR
+                                   ("Invalid port configuration specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outl(mode, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, mode);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_dio_io_single_read(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int *value, int time_out, int flags)
+{
+       me4600_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inl(instance->
+                               ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
+                                             ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+                       if ((mode ==
+                            (ME4600_DIO_CTRL_BIT_MODE_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value =
+                                   inl(instance->port_reg) & (0x1 << channel);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inl(instance->
+                               ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
+                                             ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+                       if ((mode ==
+                            (ME4600_DIO_CTRL_BIT_MODE_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value = inl(instance->port_reg) & 0xFF;
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_dio_io_single_write(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int value, int time_out, int flags)
+{
+       me4600_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t mode;
+       uint32_t byte;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inl(instance->
+                               ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
+                                             ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME4600_DIO_CTRL_BIT_MODE_0 <<
+                            (instance->dio_idx * 2))) {
+                               byte = inl(instance->port_reg) & 0xFF;
+
+                               if (value)
+                                       byte |= 0x1 << channel;
+                               else
+                                       byte &= ~(0x1 << channel);
+
+                               outl(byte, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inl(instance->
+                               ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
+                                             ME4600_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME4600_DIO_CTRL_BIT_MODE_0 <<
+                            (instance->dio_idx * 2))) {
+                               outl(value, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_dio_query_number_channels(me_subdevice_t * subdevice,
+                                           int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_dio_query_subdevice_type(me_subdevice_t * subdevice,
+                                          int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DIO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_dio_query_subdevice_caps(me_subdevice_t * subdevice,
+                                          int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_DIR_BYTE;
+       return ME_ERRNO_SUCCESS;
+}
+
+me4600_dio_subdevice_t *me4600_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock)
+{
+       me4600_dio_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me4600_dio_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_dio_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save digital i/o index */
+       subdevice->dio_idx = dio_idx;
+
+       /* Save the subdevice index */
+       subdevice->ctrl_reg = reg_base + ME4600_DIO_CTRL_REG;
+       subdevice->port_reg = reg_base + ME4600_DIO_PORT_REG + (dio_idx * 4);
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_dio_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me4600_dio_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me4600_dio_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me4600_dio_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_dio_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_dio_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_dio_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me4600_dio.h b/drivers/staging/meilhaus/me4600_dio.h
new file mode 100644 (file)
index 0000000..4625ba9
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * @file me4600_dio.h
+ *
+ * @brief ME-4000 digital input/output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_DIO_H_
+#define _ME4600_DIO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me4600_dio_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+       unsigned int dio_idx;                   /**< The index of the digital i/o on the device. */
+
+       /* Registers */
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Register to configure the port direction. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me4600_dio_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-4000 digital input/ouput subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param dio_idx The index of the digital i/o port on the device.
+ * @param ctrl_reg_lock Spin lock protecting the control register.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me4600_dio_subdevice_t *me4600_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_dio_reg.h b/drivers/staging/meilhaus/me4600_dio_reg.h
new file mode 100644 (file)
index 0000000..7a4016a
--- /dev/null
@@ -0,0 +1,63 @@
+/**
+ * @file me4600_dio_reg.h
+ *
+ * @brief ME-4000 digital input/output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_DIO_REG_H_
+#define _ME4600_DIO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME4600_DIO_PORT_0_REG                          0xA0                                    /**< Port 0 register. */
+#define ME4600_DIO_PORT_1_REG                          0xA4                                    /**< Port 1 register. */
+#define ME4600_DIO_PORT_2_REG                          0xA8                                    /**< Port 2 register. */
+#define ME4600_DIO_PORT_3_REG                          0xAC                                    /**< Port 3 register. */
+
+#define ME4600_DIO_DIR_REG                                     0xB0                                    /**< Direction register. */
+#define ME4600_DIO_PORT_REG                                    ME4600_DIO_PORT_0_REG   /**< Base for port's register. */
+
+#define ME4600_DIO_CTRL_REG                                    0xB8                                    /**< Control register. */
+/** Port A - DO */
+#define ME4600_DIO_CTRL_BIT_MODE_0                     0x0001
+#define ME4600_DIO_CTRL_BIT_MODE_1                     0x0002
+/** Port B - DI */
+#define ME4600_DIO_CTRL_BIT_MODE_2                     0x0004
+#define ME4600_DIO_CTRL_BIT_MODE_3                     0x0008
+/** Port C - DIO */
+#define ME4600_DIO_CTRL_BIT_MODE_4                     0x0010
+#define ME4600_DIO_CTRL_BIT_MODE_5                     0x0020
+/** Port D - DIO */
+#define ME4600_DIO_CTRL_BIT_MODE_6                     0x0040
+#define ME4600_DIO_CTRL_BIT_MODE_7                     0x0080
+
+#define ME4600_DIO_CTRL_BIT_FUNCTION_0         0x0100
+#define ME4600_DIO_CTRL_BIT_FUNCTION_1         0x0200
+
+#define ME4600_DIO_CTRL_BIT_FIFO_HIGH_0                0x0400
+#define ME4600_DIO_CTRL_BIT_FIFO_HIGH_1                0x0800
+#define ME4600_DIO_CTRL_BIT_FIFO_HIGH_2                0x1000
+#define ME4600_DIO_CTRL_BIT_FIFO_HIGH_3                0x2000
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_do.c b/drivers/staging/meilhaus/me4600_do.c
new file mode 100644 (file)
index 0000000..ee591bc
--- /dev/null
@@ -0,0 +1,433 @@
+/**
+ * @file me4600_do.c
+ *
+ * @brief ME-4000 digital output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me4600_dio_reg.h"
+#include "me4600_do.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me4600_do_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me4600_do_subdevice_t *instance;
+       uint32_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_do_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       /* Set port to output mode */
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inl(instance->ctrl_reg);
+       mode &= ~ME4600_DIO_CTRL_BIT_MODE_1;    //0xFFFD
+       mode |= ME4600_DIO_CTRL_BIT_MODE_0;     //0x1
+       outl(mode, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, mode);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outl(0, instance->port_reg);
+       PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_do_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me4600_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t mode;
+       int size =
+           flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
+                    | ME_IO_SINGLE_CONFIG_DIO_WORD |
+                    ME_IO_SINGLE_CONFIG_DIO_DWORD);
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inl(instance->ctrl_reg);
+
+       switch (size) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               mode &= ~(ME4600_DIO_CTRL_BIT_MODE_0 |
+                                         ME4600_DIO_CTRL_BIT_MODE_1);
+                               mode |= (ME4600_DIO_CTRL_BIT_MODE_0);
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_MUX32M) {
+                               mode &= ~(ME4600_DIO_CTRL_BIT_MODE_0 |
+                                         ME4600_DIO_CTRL_BIT_MODE_1 |
+                                         ME4600_DIO_CTRL_BIT_FUNCTION_0 |
+                                         ME4600_DIO_CTRL_BIT_FUNCTION_1 |
+                                         ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
+
+                               if (ref == ME_REF_DIO_FIFO_LOW) {
+                                       mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                                ME4600_DIO_CTRL_BIT_MODE_1 |
+                                                ME4600_DIO_CTRL_BIT_FUNCTION_1);
+                               } else if (ref == ME_REF_DIO_FIFO_HIGH) {
+                                       mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                                ME4600_DIO_CTRL_BIT_MODE_1 |
+                                                ME4600_DIO_CTRL_BIT_FUNCTION_1
+                                                |
+                                                ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
+                               } else {
+                                       PERROR
+                                           ("Invalid port reference specified.\n");
+                                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                               }
+                       } else if (single_config ==
+                                  ME_SINGLE_CONFIG_DIO_DEMUX32) {
+                               mode &=
+                                   ~(ME4600_DIO_CTRL_BIT_MODE_0 |
+                                     ME4600_DIO_CTRL_BIT_MODE_1 |
+                                     ME4600_DIO_CTRL_BIT_FUNCTION_0 |
+                                     ME4600_DIO_CTRL_BIT_FUNCTION_1 |
+                                     ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
+
+                               if (ref == ME_REF_DIO_FIFO_LOW) {
+                                       mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                                ME4600_DIO_CTRL_BIT_MODE_1 |
+                                                ME4600_DIO_CTRL_BIT_FUNCTION_0);
+                               } else if (ref == ME_REF_DIO_FIFO_HIGH) {
+                                       mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                                ME4600_DIO_CTRL_BIT_MODE_1 |
+                                                ME4600_DIO_CTRL_BIT_FUNCTION_0
+                                                |
+                                                ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
+                               } else {
+                                       PERROR
+                                           ("Invalid port reference specified.\n");
+                                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                               }
+                       } else if (single_config ==
+                                  ME_SINGLE_CONFIG_DIO_BIT_PATTERN) {
+                               mode &=
+                                   ~(ME4600_DIO_CTRL_BIT_MODE_0 |
+                                     ME4600_DIO_CTRL_BIT_MODE_1 |
+                                     ME4600_DIO_CTRL_BIT_FUNCTION_0 |
+                                     ME4600_DIO_CTRL_BIT_FUNCTION_1 |
+                                     ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
+
+                               if (ref == ME_REF_DIO_FIFO_LOW) {
+                                       mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                                ME4600_DIO_CTRL_BIT_MODE_1);
+                               } else if (ref == ME_REF_DIO_FIFO_HIGH) {
+                                       mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
+                                                ME4600_DIO_CTRL_BIT_MODE_1 |
+                                                ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
+                               } else {
+                                       PERROR
+                                           ("Invalid port reference specified.\n");
+                                       err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                               }
+                       } else {
+                               PERROR("Invalid port direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outl(mode, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, mode);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_do_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me4600_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode =
+           inl(instance->
+               ctrl_reg) & (ME4600_DIO_CTRL_BIT_MODE_0 |
+                            ME4600_DIO_CTRL_BIT_MODE_1);
+
+       if (mode == ME4600_DIO_CTRL_BIT_MODE_0) {
+               switch (flags) {
+               case ME_IO_SINGLE_TYPE_DIO_BIT:
+                       if ((channel >= 0) && (channel < 8)) {
+                               *value =
+                                   inl(instance->port_reg) & (0x1 << channel);
+                       } else {
+                               PERROR("Invalid bit number specified.\n");
+                               err = ME_ERRNO_INVALID_CHANNEL;
+                       }
+                       break;
+
+               case ME_IO_SINGLE_NO_FLAGS:
+               case ME_IO_SINGLE_TYPE_DIO_BYTE:
+                       if (channel == 0) {
+                               *value = inl(instance->port_reg) & 0xFF;
+                       } else {
+                               PERROR("Invalid byte number specified.\n");
+                               err = ME_ERRNO_INVALID_CHANNEL;
+                       }
+                       break;
+
+               default:
+                       PERROR("Invalid flags specified.\n");
+                       err = ME_ERRNO_INVALID_FLAGS;
+               }
+       } else {
+               PERROR("Port not in output mode.\n");
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_do_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me4600_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t byte;
+       uint32_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode =
+           inl(instance->
+               ctrl_reg) & (ME4600_DIO_CTRL_BIT_MODE_0 |
+                            ME4600_DIO_CTRL_BIT_MODE_1);
+
+       if (mode == ME4600_DIO_CTRL_BIT_MODE_0) {
+               switch (flags) {
+
+               case ME_IO_SINGLE_TYPE_DIO_BIT:
+                       if ((channel >= 0) && (channel < 8)) {
+                               byte = inl(instance->port_reg) & 0xFF;
+
+                               if (value)
+                                       byte |= 0x1 << channel;
+                               else
+                                       byte &= ~(0x1 << channel);
+
+                               outl(byte, instance->port_reg);
+                       } else {
+                               PERROR("Invalid bit number specified.\n");
+                               err = ME_ERRNO_INVALID_CHANNEL;
+                       }
+                       break;
+
+               case ME_IO_SINGLE_NO_FLAGS:
+               case ME_IO_SINGLE_TYPE_DIO_BYTE:
+                       if (channel == 0) {
+                               outl(value, instance->port_reg);
+                       } else {
+                               PERROR("Invalid byte number specified.\n");
+                               err = ME_ERRNO_INVALID_CHANNEL;
+                       }
+                       break;
+
+               default:
+                       PERROR("Invalid flags specified.\n");
+                       err = ME_ERRNO_INVALID_FLAGS;
+               }
+       } else {
+               PERROR("Port not in output mode.\n");
+               err = ME_ERRNO_PREVIOUS_CONFIG;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_do_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_do_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+me4600_do_subdevice_t *me4600_do_constructor(uint32_t reg_base,
+                                            spinlock_t * ctrl_reg_lock)
+{
+       me4600_do_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me4600_do_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_do_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the subdevice index */
+       subdevice->ctrl_reg = reg_base + ME4600_DIO_CTRL_REG;
+       subdevice->port_reg = reg_base + ME4600_DIO_PORT_0_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_do_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me4600_do_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me4600_do_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me4600_do_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_do_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_do_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_do_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me4600_do.h b/drivers/staging/meilhaus/me4600_do.h
new file mode 100644 (file)
index 0000000..e838564
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * @file me4600_do.h
+ *
+ * @brief ME-4000 digital output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_DO_H_
+#define _ME4600_DO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me4600_do_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Register to configure the port direction. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me4600_do_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-4000 digital output subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param ctrl_reg_lock Spin lock protecting the control register.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me4600_do_subdevice_t *me4600_do_constructor(uint32_t reg_base,
+                                            spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_ext_irq.c b/drivers/staging/meilhaus/me4600_ext_irq.c
new file mode 100644 (file)
index 0000000..8a10dce
--- /dev/null
@@ -0,0 +1,467 @@
+/**
+ * @file me4600_ext_irq.c
+ *
+ * @brief ME-4000 external interrupt subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "meids.h"
+#include "me4600_reg.h"
+#include "me4600_ai_reg.h"
+#include "me4600_ext_irq_reg.h"
+#include "me4600_ext_irq.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me4600_ext_irq_io_irq_start(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int irq_source,
+                                      int irq_edge, int irq_arg, int flags)
+{
+       me4600_ext_irq_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags;
+       uint32_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((irq_edge != ME_IRQ_EDGE_RISING)
+           && (irq_edge != ME_IRQ_EDGE_FALLING)
+           && (irq_edge != ME_IRQ_EDGE_ANY)
+           ) {
+               PERROR("Invalid irq edge specified.\n");
+               return ME_ERRNO_INVALID_IRQ_EDGE;
+       }
+
+       if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
+               PERROR("Invalid irq source specified.\n");
+               return ME_ERRNO_INVALID_IRQ_SOURCE;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       tmp = 0x0;              //inl(instance->ext_irq_config_reg);
+
+       if (irq_edge == ME_IRQ_EDGE_RISING) {
+               //tmp &= ~ME4600_EXT_IRQ_CONFIG_MASK;
+               //tmp |= ME4600_EXT_IRQ_CONFIG_MASK_RISING;
+       } else if (irq_edge == ME_IRQ_EDGE_FALLING) {
+               //tmp &= ~ME4600_EXT_IRQ_CONFIG_MASK;
+               //tmp |= ME4600_EXT_IRQ_CONFIG_MASK_FALLING;
+               tmp = ME4600_EXT_IRQ_CONFIG_MASK_FALLING;
+       } else if (irq_edge == ME_IRQ_EDGE_ANY) {
+               //tmp &= ~ME4600_EXT_IRQ_CONFIG_MASK;
+               //tmp |= ME4600_EXT_IRQ_CONFIG_MASK_ANY;
+               tmp = ME4600_EXT_IRQ_CONFIG_MASK_ANY;
+       }
+
+       outl(tmp, instance->ext_irq_config_reg);
+       PDEBUG_REG("ext_irq_config_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->ext_irq_config_reg - instance->reg_base, tmp);
+
+       spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
+       tmp = inl(instance->ctrl_reg);
+       tmp &= ~(ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
+       tmp |= ME4600_AI_CTRL_BIT_EX_IRQ;
+       outl(tmp, instance->ctrl_reg);
+       spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
+       instance->rised = 0;
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ext_irq_io_irq_wait(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int *irq_count,
+                                     int *value, int time_out, int flags)
+{
+       me4600_ext_irq_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->rised <= 0) {
+               instance->rised = 0;
+               if (time_out) {
+                       t = wait_event_interruptible_timeout(instance->
+                                                            wait_queue,
+                                                            (instance->rised !=
+                                                             0), t);
+
+                       if (t == 0) {
+                               PERROR
+                                   ("Wait on external interrupt timed out.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               } else {
+                       wait_event_interruptible(instance->wait_queue,
+                                                (instance->rised != 0));
+               }
+
+               if (instance->rised < 0) {
+                       PERROR("Wait on interrupt aborted by user.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+       }
+
+       if (signal_pending(current)) {
+               PERROR("Wait on external interrupt aborted by signal.\n");
+               err = ME_ERRNO_SIGNAL;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       instance->rised = 0;
+       *irq_count = instance->count;
+       *value = instance->value;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ext_irq_io_irq_stop(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel, int flags)
+{
+       me4600_ext_irq_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags;
+       uint32_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->ctrl_reg_lock);
+       tmp = inl(instance->ctrl_reg);
+       tmp &= ~(ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_regv outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->ctrl_reg_lock);
+       instance->rised = -1;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me4600_ext_irq_io_reset_subdevice(me_subdevice_t * subdevice,
+                                            struct file *filep, int flags)
+{
+       me4600_ext_irq_subdevice_t *instance;
+       unsigned long cpu_flags;
+       uint32_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me4600_ext_irq_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->ctrl_reg_lock);
+       tmp = inl(instance->ctrl_reg);
+       tmp &= ~(ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
+       outl(tmp, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_regv outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->ctrl_reg_lock);
+       instance->rised = -1;
+       instance->count = 0;
+       outl(ME4600_EXT_IRQ_CONFIG_MASK_ANY, instance->ext_irq_config_reg);
+       PDEBUG_REG("ext_irq_config_reg outl(0x%lX+0x%lX)=0x%x\n",
+                  instance->reg_base,
+                  instance->ext_irq_config_reg - instance->reg_base,
+                  ME4600_EXT_IRQ_CONFIG_MASK_ANY);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static void me4600_ext_irq_destructor(struct me_subdevice *subdevice)
+{
+       me4600_ext_irq_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+       instance = (me4600_ext_irq_subdevice_t *) subdevice;
+       me_subdevice_deinit(&instance->base);
+       free_irq(instance->irq, instance);
+       kfree(instance);
+}
+
+static int me4600_ext_irq_query_number_channels(me_subdevice_t * subdevice,
+                                               int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 1;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ext_irq_query_subdevice_type(me_subdevice_t * subdevice,
+                                              int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_EXT_IRQ;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ext_irq_query_subdevice_caps(me_subdevice_t * subdevice,
+                                              int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps =
+           ME_CAPS_EXT_IRQ_EDGE_RISING | ME_CAPS_EXT_IRQ_EDGE_FALLING |
+           ME_CAPS_EXT_IRQ_EDGE_ANY;
+       return ME_ERRNO_SUCCESS;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me4600_ext_irq_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me4600_ext_irq_isr(int irq, void *dev_id,
+                                     struct pt_regs *regs)
+#endif
+{
+       me4600_ext_irq_subdevice_t *instance;
+       uint32_t ctrl;
+       uint32_t irq_status;
+
+       instance = (me4600_ext_irq_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       irq_status = inl(instance->irq_status_reg);
+       if (!(irq_status & ME4600_IRQ_STATUS_BIT_EX)) {
+               PINFO("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n",
+                     jiffies, __FUNCTION__, irq_status);
+               return IRQ_NONE;
+       }
+
+       PDEBUG("executed.\n");
+
+       spin_lock(&instance->subdevice_lock);
+       instance->rised = 1;
+       instance->value = inl(instance->ext_irq_value_reg);
+       instance->count++;
+
+       spin_lock(instance->ctrl_reg_lock);
+       ctrl = inl(instance->ctrl_reg);
+       ctrl |= ME4600_AI_CTRL_BIT_EX_IRQ_RESET;
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       ctrl &= ~ME4600_AI_CTRL_BIT_EX_IRQ_RESET;
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       spin_unlock(&instance->subdevice_lock);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+me4600_ext_irq_subdevice_t *me4600_ext_irq_constructor(uint32_t reg_base,
+                                                      int irq,
+                                                      spinlock_t *
+                                                      ctrl_reg_lock)
+{
+       me4600_ext_irq_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me4600_ext_irq_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me4600_ext_irq_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       /* Register interrupt */
+       subdevice->irq = irq;
+
+       if (request_irq(subdevice->irq, me4600_ext_irq_isr,
+#ifdef IRQF_DISABLED
+                       IRQF_DISABLED | IRQF_SHARED,
+#else
+                       SA_INTERRUPT | SA_SHIRQ,
+#endif
+                       ME4600_NAME, subdevice)) {
+               PERROR("Cannot register interrupt.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       PINFO("Registered irq=%d.\n", subdevice->irq);
+
+       /* Initialize registers */
+       subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
+       subdevice->ctrl_reg = reg_base + ME4600_AI_CTRL_REG;
+       subdevice->ext_irq_config_reg = reg_base + ME4600_EXT_IRQ_CONFIG_REG;
+       subdevice->ext_irq_value_reg = reg_base + ME4600_EXT_IRQ_VALUE_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Override base class methods. */
+       subdevice->base.me_subdevice_destructor = me4600_ext_irq_destructor;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me4600_ext_irq_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_irq_start = me4600_ext_irq_io_irq_start;
+       subdevice->base.me_subdevice_io_irq_wait = me4600_ext_irq_io_irq_wait;
+       subdevice->base.me_subdevice_io_irq_stop = me4600_ext_irq_io_irq_stop;
+       subdevice->base.me_subdevice_query_number_channels =
+           me4600_ext_irq_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me4600_ext_irq_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me4600_ext_irq_query_subdevice_caps;
+
+       subdevice->rised = 0;
+       subdevice->count = 0;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me4600_ext_irq.h b/drivers/staging/meilhaus/me4600_ext_irq.h
new file mode 100644 (file)
index 0000000..3c7b27f
--- /dev/null
@@ -0,0 +1,78 @@
+/**
+ * @file me4600_ext_irq.h
+ *
+ * @brief Meilhaus ME-4000 external interrupt subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_EXT_IRQ_H_
+#define _ME4600_EXT_IRQ_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The subdevice class.
+ */
+typedef struct me4600_ext_irq_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+
+       wait_queue_head_t wait_queue;
+
+       int irq;
+
+       int rised;
+       int value;
+       int count;
+
+       unsigned long ctrl_reg;
+       unsigned long irq_status_reg;
+       unsigned long ext_irq_config_reg;
+       unsigned long ext_irq_value_reg;
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me4600_ext_irq_subdevice_t;
+
+/**
+ * @brief The constructor to generate a external interrupt subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param irq The interrupt number assigned by the PCI BIOS.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me4600_ext_irq_subdevice_t *me4600_ext_irq_constructor(uint32_t reg_base,
+                                                      int irq,
+                                                      spinlock_t *
+                                                      ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_ext_irq_reg.h b/drivers/staging/meilhaus/me4600_ext_irq_reg.h
new file mode 100644 (file)
index 0000000..898e1e7
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * @file me4600_ext_irq_reg.h
+ *
+ * @brief ME-4000 external interrupt subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_EXT_IRQ_REG_H_
+#define _ME4600_EXT_IRQ_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME4600_EXT_IRQ_CONFIG_REG              0xCC    // R/_
+#define ME4600_EXT_IRQ_VALUE_REG               0xD0    // R/_
+
+#define ME4600_EXT_IRQ_CONFIG_MASK_RISING      0x0
+#define ME4600_EXT_IRQ_CONFIG_MASK_FALLING     0x1
+#define ME4600_EXT_IRQ_CONFIG_MASK_ANY         0x3
+#define ME4600_EXT_IRQ_CONFIG_MASK             0x3
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me4600_reg.h b/drivers/staging/meilhaus/me4600_reg.h
new file mode 100644 (file)
index 0000000..ae152bb
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * @file me4600_reg.h
+ *
+ * @brief ME-4000 register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME4600_REG_H_
+#define _ME4600_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME4600_IRQ_STATUS_REG                  0x9C    // R/_
+
+#define ME4600_IRQ_STATUS_BIT_EX               0x01
+#define ME4600_IRQ_STATUS_BIT_LE               0x02
+#define ME4600_IRQ_STATUS_BIT_AI_HF            0x04
+#define ME4600_IRQ_STATUS_BIT_AO_0_HF  0x08
+#define ME4600_IRQ_STATUS_BIT_AO_1_HF  0x10
+#define ME4600_IRQ_STATUS_BIT_AO_2_HF  0x20
+#define ME4600_IRQ_STATUS_BIT_AO_3_HF  0x40
+#define ME4600_IRQ_STATUS_BIT_SC               0x80
+
+#define ME4600_IRQ_STATUS_BIT_AO_HF            ME4600_IRQ_STATUS_BIT_AO_0_HF
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me6000_ao.c b/drivers/staging/meilhaus/me6000_ao.c
new file mode 100644 (file)
index 0000000..3f5ff6d
--- /dev/null
@@ -0,0 +1,3739 @@
+/**
+ * @file me6000_ao.c
+ *
+ * @brief ME-6000 analog output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/* Includes
+ */
+#include <linux/version.h>
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/workqueue.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "meids.h"
+#include "me6000_reg.h"
+#include "me6000_ao_reg.h"
+#include "me6000_ao.h"
+
+/* Defines
+ */
+
+static int me6000_ao_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range);
+
+static int me6000_ao_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count);
+
+static int me6000_ao_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata);
+
+static int me6000_ao_query_timer(me_subdevice_t * subdevice,
+                                int timer,
+                                int *base_frequency,
+                                long long *min_ticks, long long *max_ticks);
+
+static int me6000_ao_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number);
+
+static int me6000_ao_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype);
+
+static int me6000_ao_query_subdevice_caps(me_subdevice_t * subdevice,
+                                         int *caps);
+
+static int me6000_ao_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                              int cap, int *args, int count);
+
+/** Remove subdevice. */
+static void me6000_ao_destructor(struct me_subdevice *subdevice);
+
+/** Reset subdevice. Stop all actions. Reset registry. Disable FIFO. Set output to 0V and status to 'none'. */
+static int me6000_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags);
+
+/** Set output as single */
+static int me6000_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags);
+
+/** Pass to user actual value of output. */
+static int me6000_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags);
+
+/** Write to output requed value. */
+static int me6000_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags);
+
+/** Set output as streamed device. */
+static int me6000_ao_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags);
+
+/** Wait for / Check empty space in buffer. */
+static int me6000_ao_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags);
+
+/** Start streaming. */
+static int me6000_ao_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags);
+
+/** Check actual state. / Wait for end. */
+static int me6000_ao_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags);
+
+/** Stop streaming. */
+static int me6000_ao_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags);
+
+/** Write datas to buffor. */
+static int me6000_ao_io_stream_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int write_mode,
+                                    int *values, int *count, int flags);
+
+/** Interrupt handler. Copy from buffer to FIFO. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me6000_ao_isr(int irq, void *dev_id);
+#else
+static irqreturn_t me6000_ao_isr(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+
+/** Copy data from circular buffer to fifo (fast) in wraparound mode. */
+int inline ao_write_data_wraparound(me6000_ao_subdevice_t * instance, int count,
+                                   int start_pos);
+
+/** Copy data from circular buffer to fifo (fast).*/
+int inline ao_write_data(me6000_ao_subdevice_t * instance, int count,
+                        int start_pos);
+
+/** Copy data from circular buffer to fifo (slow).*/
+int inline ao_write_data_pooling(me6000_ao_subdevice_t * instance, int count,
+                                int start_pos);
+
+/** Copy data from user space to circular buffer. */
+int inline ao_get_data_from_user(me6000_ao_subdevice_t * instance, int count,
+                                int *user_values);
+
+/** Stop presentation. Preserve FIFOs. */
+int inline ao_stop_immediately(me6000_ao_subdevice_t * instance);
+
+/** Function for checking timeout in non-blocking mode. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+static void me6000_ao_work_control_task(void *subdevice);
+#else
+static void me6000_ao_work_control_task(struct work_struct *work);
+#endif
+
+/* Functions
+ */
+
+static int me6000_ao_io_reset_subdevice(me_subdevice_t * subdevice,
+                                       struct file *filep, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t tmp;
+       uint32_t ctrl;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       instance->status = ao_status_none;
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+       instance->timeout.delay = 0;
+       instance->timeout.start_time = jiffies;
+
+       //Stop state machine.
+       err = ao_stop_immediately(instance);
+
+       //Remove from synchronous start.
+       spin_lock(instance->preload_reg_lock);
+       tmp = inl(instance->preload_reg);
+       tmp &=
+           ~((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) << instance->
+             ao_idx);
+       outl(tmp, instance->preload_reg);
+       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, tmp);
+       *instance->preload_flags &=
+           ~((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) << instance->
+             ao_idx);
+
+       //Reset triggering flag
+       *instance->triggering_flags &= ~(0x1 << instance->ao_idx);
+       spin_unlock(instance->preload_reg_lock);
+
+       if (instance->fifo) {
+               //Set single mode, dissable FIFO, dissable external trigger, block interrupt.
+               ctrl = ME6000_AO_MODE_SINGLE;
+
+               //Block ISM.
+               ctrl |=
+                   (ME6000_AO_CTRL_BIT_STOP |
+                    ME6000_AO_CTRL_BIT_IMMEDIATE_STOP);
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               //Set speed
+               outl(ME6000_AO_MIN_CHAN_TICKS - 1, instance->timer_reg);
+               //Reset interrupt latch
+               inl(instance->irq_reset_reg);
+       }
+
+       instance->hardware_stop_delay = HZ / 10;        //100ms
+
+       //Set output to 0V
+       outl(0x8000, instance->single_reg);
+       PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->single_reg - instance->reg_base, 0x8000);
+
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+       instance->preloaded_count = 0;
+       instance->data_count = 0;
+       instance->single_value = 0x8000;
+       instance->single_value_in_fifo = 0x8000;
+
+       //Set status to signal that device is unconfigured.
+       instance->status = ao_status_none;
+       //Signal reset if user is on wait.
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t ctrl;
+       uint32_t sync;
+       unsigned long cpu_flags;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. ID=%d\n", instance->ao_idx);
+
+       // Checking parameters
+       if (flags) {
+               PERROR
+                   ("Invalid flag specified. Must be ME_IO_SINGLE_CONFIG_NO_FLAGS.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (instance->fifo) {   //Stream hardware (with or without fifo)
+               if ((trig_edge == ME_TRIG_TYPE_SW)
+                   && (trig_edge != ME_TRIG_EDGE_NONE)) {
+                       PERROR
+                           ("Invalid trigger edge. Software trigger has not edge.\n");
+                       return ME_ERRNO_INVALID_TRIG_EDGE;
+               }
+
+               if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL) {
+                       switch (trig_edge) {
+                       case ME_TRIG_EDGE_ANY:
+                       case ME_TRIG_EDGE_RISING:
+                       case ME_TRIG_EDGE_FALLING:
+                               break;
+
+                       default:
+                               PERROR("Invalid trigger edge.\n");
+                               return ME_ERRNO_INVALID_TRIG_EDGE;
+                       }
+               }
+
+               if ((trig_type != ME_TRIG_TYPE_SW)
+                   && (trig_type != ME_TRIG_TYPE_EXT_DIGITAL)) {
+                       PERROR
+                           ("Invalid trigger type. Trigger must be software or digital.\n");
+                       return ME_ERRNO_INVALID_TRIG_TYPE;
+               }
+       } else {                //Single
+               if (trig_edge != ME_TRIG_EDGE_NONE) {
+                       PERROR
+                           ("Invalid trigger edge. Single output trigger hasn't own edge.\n");
+                       return ME_ERRNO_INVALID_TRIG_EDGE;
+               }
+
+               if (trig_type != ME_TRIG_TYPE_SW) {
+                       PERROR
+                           ("Invalid trigger type. Trigger must be software.\n");
+                       return ME_ERRNO_INVALID_TRIG_TYPE;
+               }
+
+       }
+
+       if ((trig_chan != ME_TRIG_CHAN_DEFAULT)
+           && (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS)) {
+               PERROR("Invalid trigger channel specified.\n");
+               return ME_ERRNO_INVALID_TRIG_CHAN;
+       }
+/*
+       if ((trig_type == ME_TRIG_TYPE_EXT_DIGITAL) && (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS))
+       {
+               PERROR("Invalid trigger channel specified. Must be synchronous when digital is choose.\n");
+               return ME_ERRNO_INVALID_TRIG_CHAN;
+       }
+*/
+       if (ref != ME_REF_AO_GROUND) {
+               PERROR
+                   ("Invalid reference. Analog outputs have to have got REF_AO_GROUND.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if (single_config != 0) {
+               PERROR
+                   ("Invalid single config specified. Only one range for anlog outputs is available.\n");
+               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+       }
+
+       if (channel != 0) {
+               PERROR
+                   ("Invalid channel number specified. Analog output have only one channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Subdevice running in stream mode!
+       if ((instance->status >= ao_status_stream_run_wait)
+           && (instance->status < ao_status_stream_end)) {
+               PERROR("Subdevice is busy.\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+/// @note For single all calls (config and write) are erasing previous state!
+
+       instance->status = ao_status_none;
+
+       // Correct single mirrors
+       instance->single_value_in_fifo = instance->single_value;
+
+       //Stop device
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       if (instance->fifo) {   // Set control register.
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               // Set stop bit. Stop streaming mode (If running.).
+               ctrl = inl(instance->ctrl_reg);
+               //Reset all bits.
+               ctrl =
+                   ME6000_AO_CTRL_BIT_IMMEDIATE_STOP | ME6000_AO_CTRL_BIT_STOP;
+               if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL) {
+                       PINFO("External digital trigger.\n");
+
+                       if (trig_edge == ME_TRIG_EDGE_ANY) {
+//                                      ctrl |= ME6000_AO_CTRL_BIT_EX_TRIG_EDGE | ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                               instance->ctrl_trg =
+                                   ME6000_AO_CTRL_BIT_EX_TRIG_EDGE |
+                                   ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                       } else if (trig_edge == ME_TRIG_EDGE_FALLING) {
+//                                      ctrl |= ME6000_AO_CTRL_BIT_EX_TRIG_EDGE;
+                               instance->ctrl_trg =
+                                   ME6000_AO_CTRL_BIT_EX_TRIG_EDGE;
+                       } else if (trig_edge == ME_TRIG_EDGE_RISING) {
+                               instance->ctrl_trg = 0x0;
+                       }
+               } else if (trig_type == ME_TRIG_TYPE_SW) {
+                       PDEBUG("SOFTWARE TRIGGER\n");
+                       instance->ctrl_trg = 0x0;
+               }
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       } else {
+               PDEBUG("SOFTWARE TRIGGER\n");
+       }
+
+       // Set preload/synchronization register.
+       spin_lock(instance->preload_reg_lock);
+
+       if (trig_type == ME_TRIG_TYPE_SW) {
+               *instance->preload_flags &=
+                   ~(ME6000_AO_SYNC_EXT_TRIG << instance->ao_idx);
+       } else                  //if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL)
+       {
+               *instance->preload_flags |=
+                   ME6000_AO_SYNC_EXT_TRIG << instance->ao_idx;
+       }
+
+       if (trig_chan == ME_TRIG_CHAN_DEFAULT) {
+               *instance->preload_flags &=
+                   ~(ME6000_AO_SYNC_HOLD << instance->ao_idx);
+       } else                  //if (trig_chan == ME_TRIG_CHAN_SYNCHRONOUS)
+       {
+               *instance->preload_flags |=
+                   ME6000_AO_SYNC_HOLD << instance->ao_idx;
+       }
+
+       //Reset hardware register
+       sync = inl(instance->preload_reg);
+       PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, sync);
+       sync &= ~(ME6000_AO_SYNC_EXT_TRIG << instance->ao_idx);
+       sync |= ME6000_AO_SYNC_HOLD << instance->ao_idx;
+
+       //Output configured in default mode (safe one)
+       outl(sync, instance->preload_reg);
+       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, sync);
+       spin_unlock(instance->preload_reg_lock);
+
+       instance->status = ao_status_single_configured;
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       unsigned long j;
+       unsigned long delay = 0;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags & ~ME_IO_SINGLE_NONBLOCKING) {
+               PERROR("Invalid flag specified. %d\n", flags);
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((instance->status >= ao_status_stream_configured)
+           && (instance->status <= ao_status_stream_end)) {
+               PERROR("Subdevice not configured to work in single mode!\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       if (channel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       ME_SUBDEVICE_ENTER;
+       if ((!flags) && (instance->status == ao_status_single_run_wait)) {      //Blocking mode. Wait for trigger.
+               if (time_out) {
+                       delay = (time_out * HZ) / 1000;
+                       if (delay == 0)
+                               delay = 1;
+               }
+
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_single_run_wait),
+                                                (delay) ? delay : LONG_MAX);
+
+               if (instance->status == ao_status_none) {
+                       PDEBUG("Single canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - j) >= delay)) {
+                       PDEBUG("Timeout reached.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               }
+
+               *value =
+                   (!err) ? instance->single_value_in_fifo : instance->
+                   single_value;
+       } else {                //Non-blocking mode
+               //Read value
+               *value = instance->single_value;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags;
+       unsigned long j;
+       unsigned long delay = 0;
+
+       uint32_t sync_mask;
+       uint32_t mode;
+
+       uint32_t tmp;
+
+/// Workaround for mix-mode - begin
+       uint32_t ctrl = 0x0;
+       uint32_t status;
+/// Workaround for mix-mode - end
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags &
+           ~(ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS |
+             ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((instance->status == ao_status_none)
+           || (instance->status > ao_status_single_end)) {
+               PERROR("Subdevice not configured to work in single mode!\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       if (channel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (value & ~ME6000_AO_MAX_DATA) {
+               PERROR("Invalid value provided.\n");
+               return ME_ERRNO_VALUE_OUT_OF_RANGE;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+/// @note For single all calls (config and write) are erasing previous state!
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       // Correct single mirrors
+       instance->single_value_in_fifo = instance->single_value;
+
+       //Stop device
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+
+       instance->single_value_in_fifo = value;
+
+       if (instance->fifo) {
+               ctrl = inl(instance->ctrl_reg);
+       }
+
+       if (instance->fifo & ME6000_AO_HAS_FIFO) {      /// Workaround for mix-mode - begin
+               //Set speed
+               outl(ME6000_AO_MIN_CHAN_TICKS - 1, instance->timer_reg);
+               PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->timer_reg - instance->reg_base,
+                          (int)ME6000_AO_MIN_CHAN_TICKS);
+               instance->hardware_stop_delay = HZ / 10;        //100ms
+
+               status = inl(instance->status_reg);
+
+               //Set the continous mode.
+               ctrl &= ~ME6000_AO_CTRL_MODE_MASK;
+               ctrl |= ME6000_AO_MODE_CONTINUOUS;
+
+               //Prepare FIFO
+               if (!(ctrl & ME6000_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it.
+                       PINFO("Enableing FIFO.\n");
+                       ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       ctrl |= ME6000_AO_CTRL_BIT_ENABLE_FIFO;
+               } else {        //Check if FIFO is empty
+                       if (status & ME6000_AO_STATUS_BIT_EF) { //FIFO not empty
+                               PINFO("Reseting FIFO.\n");
+                               ctrl &=
+                                   ~(ME6000_AO_CTRL_BIT_ENABLE_FIFO |
+                                     ME6000_AO_CTRL_BIT_ENABLE_IRQ);
+                               outl(ctrl, instance->ctrl_reg);
+                               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->ctrl_reg -
+                                          instance->reg_base, ctrl);
+
+                               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_FIFO;
+                       } else {        //FIFO empty, only interrupt needs to be disabled!
+                               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       }
+               }
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               //Reset interrupt latch
+               inl(instance->irq_reset_reg);
+
+               //Write output - 1 value to FIFO
+               if (instance->ao_idx & 0x1) {
+                       outl(value <<= 16, instance->fifo_reg);
+                       PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->fifo_reg - instance->reg_base,
+                                  value <<= 16);
+               } else {
+                       outl(value, instance->fifo_reg);
+                       PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->fifo_reg - instance->reg_base,
+                                  value);
+               }
+               /// Workaround for mix-mode - end
+       } else {                //No FIFO - always in single mode
+               //Write value
+               PDEBUG("Write value\n");
+               outl(value, instance->single_reg);
+               PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->single_reg - instance->reg_base, value);
+       }
+
+       mode = *instance->preload_flags >> instance->ao_idx;
+       mode &= (ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG);
+
+       PINFO("Triggering mode: 0x%08x\n", mode);
+
+       spin_lock(instance->preload_reg_lock);
+       sync_mask = inl(instance->preload_reg);
+       PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, sync_mask);
+       switch (mode) {
+       case 0:         //0x00000000: Individual software
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               if (instance->fifo & ME6000_AO_HAS_FIFO) {      // FIFO - Continous mode
+                       ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+                       if ((sync_mask & ((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) {       //Now we can set correct mode.
+                               sync_mask &=
+                                   ~((ME6000_AO_SYNC_EXT_TRIG |
+                                      ME6000_AO_SYNC_HOLD) << instance->
+                                     ao_idx);
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               } else {        // No FIFO - Single mode: In this case resetting 'ME6000_AO_SYNC_HOLD' will trigger output.
+                       if ((sync_mask & ((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME6000_AO_SYNC_HOLD) {       //Now we can set correct mode. This is exception. It is set to synchronous and triggered later.
+                               sync_mask &=
+                                   ~(ME6000_AO_SYNC_EXT_TRIG << instance->
+                                     ao_idx);
+                               sync_mask |=
+                                   ME6000_AO_SYNC_HOLD << instance->ao_idx;
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               }
+               instance->single_value = value;
+               break;
+
+       case ME6000_AO_SYNC_EXT_TRIG:   //0x00010000: Individual hardware
+               PDEBUG("DIGITAL TRIGGER\n");
+               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               if (instance->fifo & ME6000_AO_HAS_FIFO) {      // FIFO - Continous mode
+                       if ((sync_mask & ((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) {       //Now we can set correct mode.
+                               sync_mask &=
+                                   ~((ME6000_AO_SYNC_EXT_TRIG |
+                                      ME6000_AO_SYNC_HOLD) << instance->
+                                     ao_idx);
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               } else {        // No FIFO - Single mode
+                       if ((sync_mask &
+                            ((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) <<
+                             instance->ao_idx)) != ME6000_AO_SYNC_HOLD) {
+                               //Now we can set correct mode
+                               sync_mask &=
+                                   ~(ME6000_AO_SYNC_EXT_TRIG << instance->
+                                     ao_idx);
+                               sync_mask |=
+                                   ME6000_AO_SYNC_HOLD << instance->ao_idx;
+
+                               outl(sync_mask, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    sync_mask);
+                       }
+               }
+               break;
+
+       case ME6000_AO_SYNC_HOLD:       //0x00000001: Synchronous software
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               if ((sync_mask &
+                    ((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) <<
+                     instance->ao_idx)) !=
+                   (ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG)) {
+                       //Now we can set correct mode
+                       sync_mask |=
+                           ME6000_AO_SYNC_EXT_TRIG << instance->ao_idx;
+                       sync_mask |= ME6000_AO_SYNC_HOLD << instance->ao_idx;
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               }
+               //Set triggering flag
+               *instance->triggering_flags |= 0x1 << instance->ao_idx;
+               break;
+
+       case (ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG):   //0x00010001: Synchronous hardware
+               PDEBUG("DIGITAL TRIGGER\n");
+               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+
+               if ((sync_mask &
+                    ((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) <<
+                     instance->ao_idx)) !=
+                   (ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG)) {
+                       //Now we can set correct mode
+                       sync_mask |=
+                           (ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) <<
+                           instance->ao_idx;
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+               }
+               //Set triggering flag
+               *instance->triggering_flags |= 0x1 << instance->ao_idx;
+               break;
+       }
+//              spin_unlock(instance->preload_reg_lock);        // Moved down.
+
+       if (instance->fifo) {   //Activate ISM (remove 'stop' bits)
+               ctrl &=
+                   ~(ME6000_AO_CTRL_BIT_EX_TRIG_EDGE |
+                     ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
+               ctrl |= instance->ctrl_trg;
+               ctrl &=
+                   ~(ME6000_AO_CTRL_BIT_STOP |
+                     ME6000_AO_CTRL_BIT_IMMEDIATE_STOP);
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+       }
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+/// @note When flag 'ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS' is set than output is triggered. ALWAYS!
+
+       PINFO("<%s> start mode= 0x%08x %s\n", __FUNCTION__, mode,
+             (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) ? "SYNCHRONOUS" :
+             "");
+       if (instance->fifo & ME6000_AO_HAS_FIFO) {      // FIFO - Continous mode
+               if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       //Trigger outputs
+                       //Add channel to start list
+                       outl(sync_mask |
+                            (ME6000_AO_SYNC_HOLD << instance->ao_idx),
+                            instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask | (ME6000_AO_SYNC_HOLD <<
+                                               instance->ao_idx));
+
+                       //Fire
+                       PINFO
+                           ("Fired all software synchronous outputs by software trigger.\n");
+                       outl(0x8000, instance->single_reg);
+                       PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->single_reg - instance->reg_base,
+                                  0x8000);
+
+                       //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+
+               } else if (!mode) {     //Trigger outputs
+/*                     //Remove channel from start list
+                       outl(sync_mask & ~(ME6000_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, sync_mask & ~(ME6000_AO_SYNC_HOLD << instance->ao_idx));
+*/
+                       //Fire
+                       PINFO("Software trigger.\n");
+                       outl(0x8000, instance->single_reg);
+                       PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->single_reg - instance->reg_base,
+                                  0x8000);
+
+/*                     //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, sync_mask);
+*/
+               }
+/// @note This is mix-mode case. For now I do not have possibility to trigger first 4 channels (continous mode) and other (single) ones at once.
+/// @note Because triggering is not working it can not be add to synchronous list. First 4 channels don't need this information, anyway.
+               *instance->triggering_flags &= 0xFFFFFFF0;
+       } else {                // No FIFO - Single mode
+               if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       //Fired all software synchronous outputs.
+                       tmp = ~(*instance->preload_flags | 0xFFFF0000);
+                       PINFO
+                           ("Fired all software synchronous outputs. mask:0x%08x\n",
+                            tmp);
+                       tmp |= sync_mask & 0xFFFF0000;
+                       // Add this channel to list
+                       tmp &= ~(ME6000_AO_SYNC_HOLD << instance->ao_idx);
+
+                       //Fire
+                       PINFO("Software trigger.\n");
+                       outl(tmp, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  tmp);
+
+                       //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+
+                       //Set all as triggered.
+                       *instance->triggering_flags = 0x0;
+               } else if (!mode) {     // Add this channel to list
+                       outl(sync_mask &
+                            ~(ME6000_AO_SYNC_HOLD << instance->ao_idx),
+                            instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask & ~(ME6000_AO_SYNC_HOLD <<
+                                                instance->ao_idx));
+
+                       //Fire
+                       PINFO("Software trigger.\n");
+
+                       //Restore save settings
+                       outl(sync_mask, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  sync_mask);
+
+                       //Set all as triggered.
+                       *instance->triggering_flags = 0x0;
+               }
+
+       }
+       spin_unlock(instance->preload_reg_lock);
+
+       instance->status = ao_status_single_run_wait;
+
+       instance->timeout.delay = delay;
+       instance->timeout.start_time = jiffies;
+       instance->ao_control_task_flag = 1;
+       queue_delayed_work(instance->me6000_workqueue,
+                          &instance->ao_control_task, 1);
+
+       if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_single_run_wait),
+                                                (delay) ? delay +
+                                                1 : LONG_MAX);
+
+               if (instance->status != ao_status_single_end) {
+                       PDEBUG("Single canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->ao_control_task_flag = 0;
+                       cancel_delayed_work(&instance->ao_control_task);
+                       ao_stop_immediately(instance);
+                       instance->status = ao_status_none;
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - j) >= delay)) {
+                       if (instance->status == ao_status_single_end) {
+                               PDEBUG("Timeout reached.\n");
+                       } else if ((jiffies - j) > delay) {
+                               PERROR
+                                   ("Timeout reached. Not handled by control task!\n");
+                               ao_stop_immediately(instance);
+                       } else {
+                               PERROR
+                                   ("Timeout reached. Signal come but status is strange: %d\n",
+                                    instance->status);
+                               ao_stop_immediately(instance);
+                       }
+
+                       instance->ao_control_task_flag = 0;
+                       cancel_delayed_work(&instance->ao_control_task);
+                       instance->status = ao_status_single_end;
+                       err = ME_ERRNO_TIMEOUT;
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_stream_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t ctrl;
+       unsigned long cpu_flags;
+       uint64_t conv_ticks;
+       unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow;
+       unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       conv_ticks =
+           (uint64_t) conv_start_ticks_low +
+           ((uint64_t) conv_start_ticks_high << 32);
+
+       if (flags &
+           ~(ME_IO_STREAM_CONFIG_HARDWARE_ONLY |
+             ME_IO_STREAM_CONFIG_WRAPAROUND)) {
+               PERROR("Invalid flags.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) {
+               if (!flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       PERROR
+                           ("Hardware ME_IO_STREAM_CONFIG_HARDWARE_ONLY has to be with ME_IO_STREAM_CONFIG_WRAPAROUND.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+
+               if ((trigger->iAcqStopTrigType != ME_TRIG_TYPE_NONE)
+                   || (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE)) {
+                       PERROR
+                           ("Hardware wraparound mode must be in infinite mode.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+       }
+
+       if (count != 1) {
+               PERROR("Only 1 entry in config list acceptable.\n");
+               return ME_ERRNO_INVALID_CONFIG_LIST_COUNT;
+       }
+
+       if (config_list[0].iChannel != 0) {
+               PERROR("Invalid channel number specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (config_list[0].iStreamConfig != 0) {
+               PERROR("Only one range available.\n");
+               return ME_ERRNO_INVALID_STREAM_CONFIG;
+       }
+
+       if (config_list[0].iRef != ME_REF_AO_GROUND) {
+               PERROR("Output is referenced to ground.\n");
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       if ((trigger->iAcqStartTicksLow != 0)
+           || (trigger->iAcqStartTicksHigh != 0)) {
+               PERROR
+                   ("Invalid acquisition start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_ARG;
+       }
+
+       if (config_list[0].iFlags) {
+               PERROR("Invalid config list flag.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((trigger->iAcqStartTrigType != ME_TRIG_TYPE_SW)
+           && (trigger->iAcqStartTrigType != ME_TRIG_TYPE_EXT_DIGITAL)) {
+               PERROR("Invalid acquisition start trigger type specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;
+       }
+
+       if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {
+               switch (trigger->iAcqStartTrigEdge) {
+               case ME_TRIG_EDGE_RISING:
+               case ME_TRIG_EDGE_FALLING:
+               case ME_TRIG_EDGE_ANY:
+                       break;
+
+               default:
+                       PERROR
+                           ("Invalid acquisition start trigger edge specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+               }
+       }
+
+       if ((trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW)
+           && (trigger->iAcqStartTrigEdge != ME_TRIG_TYPE_NONE)) {
+               PERROR("Invalid acquisition start trigger edge specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
+       }
+
+       if (trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW) {
+               PERROR("Invalid scan start trigger type specified.\n");
+               return ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
+       }
+
+       if (trigger->iConvStartTrigType != ME_TRIG_TYPE_TIMER) {
+               PERROR("Invalid conv start trigger type specified.\n");
+               return ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
+       }
+
+       if ((conv_ticks < ME6000_AO_MIN_CHAN_TICKS)
+           || (conv_ticks > ME6000_AO_MAX_CHAN_TICKS)) {
+               PERROR("Invalid conv start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_CONV_START_ARG;
+       }
+
+       if (trigger->iAcqStartTicksLow || trigger->iAcqStartTicksHigh) {
+               PERROR("Invalid acq start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_ARG;
+       }
+
+       if (trigger->iScanStartTicksLow || trigger->iScanStartTicksHigh) {
+               PERROR("Invalid scan start trigger argument specified.\n");
+               return ME_ERRNO_INVALID_SCAN_START_ARG;
+       }
+
+       switch (trigger->iScanStopTrigType) {
+       case ME_TRIG_TYPE_NONE:
+               if (trigger->iScanStopCount != 0) {
+                       PERROR("Invalid scan stop count specified.\n");
+                       return ME_ERRNO_INVALID_SCAN_STOP_ARG;
+               }
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       if (trigger->iScanStopCount <= 0) {
+                               PERROR("Invalid scan stop count specified.\n");
+                               return ME_ERRNO_INVALID_SCAN_STOP_ARG;
+                       }
+               } else {
+                       PERROR("The continous mode has not 'scan' contects.\n");
+                       return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+               }
+               break;
+
+       default:
+               PERROR("Invalid scan stop trigger type specified.\n");
+               return ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE;
+       }
+
+       switch (trigger->iAcqStopTrigType) {
+       case ME_TRIG_TYPE_NONE:
+               if (trigger->iAcqStopCount != 0) {
+                       PERROR("Invalid acq stop count specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_STOP_ARG;
+               }
+               break;
+
+       case ME_TRIG_TYPE_COUNT:
+               if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
+                       PERROR("Invalid acq stop trigger type specified.\n");
+                       return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+               }
+
+               if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
+                       if (trigger->iAcqStopCount <= 0) {
+                               PERROR
+                                   ("The continous mode has not 'scan' contects.\n");
+                               return ME_ERRNO_INVALID_ACQ_STOP_ARG;
+                       }
+               }
+//                      else
+//                      {
+//                              PERROR("Invalid acq stop trigger type specified.\n");
+//                              return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+//                      }
+
+               break;
+
+       default:
+               PERROR("Invalid acq stop trigger type specified.\n");
+               return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
+       }
+
+       switch (trigger->iAcqStartTrigChan) {
+       case ME_TRIG_CHAN_DEFAULT:
+       case ME_TRIG_CHAN_SYNCHRONOUS:
+               break;
+
+       default:
+               PERROR("Invalid acq start trigger channel specified.\n");
+               return ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Stop device
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       //Check if state machine is stopped.
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       //Reset control register. Block all actions. Disable IRQ. Disable FIFO.
+       ctrl = ME6000_AO_CTRL_BIT_IMMEDIATE_STOP | ME6000_AO_CTRL_BIT_STOP;
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+
+       //Reset interrupt latch
+       inl(instance->irq_reset_reg);
+
+       //This is paranoic, but to be sure.
+       instance->preloaded_count = 0;
+       instance->data_count = 0;
+       instance->circ_buf.head = 0;
+       instance->circ_buf.tail = 0;
+
+       /* Set mode. */
+       if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {   //Wraparound
+               if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) {        //Hardware wraparound
+                       PINFO("Hardware wraparound.\n");
+                       ctrl |= ME6000_AO_MODE_WRAPAROUND;
+                       instance->mode = ME6000_AO_HW_WRAP_MODE;
+               } else {        //Software wraparound
+                       PINFO("Software wraparound.\n");
+                       ctrl |= ME6000_AO_MODE_CONTINUOUS;
+                       instance->mode = ME6000_AO_SW_WRAP_MODE;
+               }
+       } else {                //Continous
+               PINFO("Continous.\n");
+               ctrl |= ME6000_AO_MODE_CONTINUOUS;
+               instance->mode = ME6000_AO_CONTINOUS;
+       }
+
+       //Set the trigger edge.
+       if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {   //Set the trigger type and edge for external trigger.
+               PINFO("External digital trigger.\n");
+               instance->start_mode = ME6000_AO_EXT_TRIG;
+
+               switch (trigger->iAcqStartTrigEdge) {
+               case ME_TRIG_EDGE_RISING:
+                       PINFO("Set the trigger edge: rising.\n");
+                       instance->ctrl_trg = 0x0;
+                       break;
+
+               case ME_TRIG_EDGE_FALLING:
+                       PINFO("Set the trigger edge: falling.\n");
+//                                      ctrl |= ME6000_AO_CTRL_BIT_EX_TRIG_EDGE;
+                       instance->ctrl_trg = ME6000_AO_CTRL_BIT_EX_TRIG_EDGE;
+                       break;
+
+               case ME_TRIG_EDGE_ANY:
+                       PINFO("Set the trigger edge: both edges.\n");
+//                                      ctrl |= ME6000_AO_CTRL_BIT_EX_TRIG_EDGE | ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                       instance->ctrl_trg =
+                           ME6000_AO_CTRL_BIT_EX_TRIG_EDGE |
+                           ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
+                       break;
+               }
+       } else {
+               PINFO("Internal software trigger.\n");
+               instance->start_mode = 0;
+       }
+
+       //Set the stop mode and value.
+       if (trigger->iAcqStopTrigType == ME_TRIG_TYPE_COUNT) {  //Amount of data
+               instance->stop_mode = ME6000_AO_ACQ_STOP_MODE;
+               instance->stop_count = trigger->iAcqStopCount;
+       } else if (trigger->iScanStopTrigType == ME_TRIG_TYPE_COUNT) {  //Amount of 'scans'
+               instance->stop_mode = ME6000_AO_SCAN_STOP_MODE;
+               instance->stop_count = trigger->iScanStopCount;
+       } else {                //Infinite
+               instance->stop_mode = ME6000_AO_INF_STOP_MODE;
+               instance->stop_count = 0;
+       }
+
+       PINFO("Stop count: %d.\n", instance->stop_count);
+
+       if (trigger->iAcqStartTrigChan == ME_TRIG_CHAN_SYNCHRONOUS) {   //Synchronous start
+               instance->start_mode |= ME6000_AO_SYNC_HOLD;
+               if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {   //Externaly triggered
+                       PINFO("Synchronous start. Externaly trigger active.\n");
+                       instance->start_mode |= ME6000_AO_SYNC_EXT_TRIG;
+               }
+#ifdef MEDEBUG_INFO
+               else {
+                       PINFO
+                           ("Synchronous start. Externaly trigger dissabled.\n");
+               }
+#endif
+
+       }
+       //Set speed
+       outl(conv_ticks - 2, instance->timer_reg);
+       PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%llx\n", instance->reg_base,
+                  instance->timer_reg - instance->reg_base, conv_ticks - 2);
+       instance->hardware_stop_delay = (int)(conv_ticks * HZ) / ME6000_AO_BASE_FREQUENCY;      //<== MUST be with cast!
+
+       // Write the control word
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+
+       //Set status.
+       instance->status = ao_status_stream_configured;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_stream_new_values(me_subdevice_t * subdevice,
+                                         struct file *filep,
+                                         int time_out, int *count, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       long j;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!instance->circ_buf.buf) {
+               PERROR("Circular buffer not exists.\n");
+               return ME_ERRNO_INTERNAL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (me_circ_buf_space(&instance->circ_buf)) {   //The buffer is NOT full.
+               *count = me_circ_buf_space(&instance->circ_buf);
+       } else {                //The buffer is full.
+               if (time_out) {
+                       t = (time_out * HZ) / 1000;
+
+                       if (t == 0)
+                               t = 1;
+               } else {        //Max time.
+                       t = LONG_MAX;
+               }
+
+               *count = 0;
+
+               j = jiffies;
+
+               //Only runing process will interrupt this call. Interrupts are when FIFO HF is signaled.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((me_circ_buf_space
+                                                  (&instance->circ_buf))
+                                                 || !(inl(instance->status_reg)
+                                                      &
+                                                      ME6000_AO_STATUS_BIT_FSM)),
+                                                t);
+
+               if (!(inl(instance->status_reg) & ME6000_AO_STATUS_BIT_FSM)) {
+                       PERROR("AO subdevice is not running.\n");
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+               } else if (signal_pending(current)) {
+                       PERROR("Wait on values interrupted from signal.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               } else if ((jiffies - j) >= t) {
+                       PERROR("Wait on values timed out.\n");
+                       err = ME_ERRNO_TIMEOUT;
+               } else {        //Uff... all is good. Inform user about empty space.
+                       *count = me_circ_buf_space(&instance->circ_buf);
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_stream_start(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int start_mode, int time_out, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long cpu_flags = 0;
+       uint32_t status;
+       uint32_t ctrl;
+       uint32_t synch;
+       int count = 0;
+       int circ_buffer_count;
+
+       unsigned long ref;
+       unsigned long delay = 0;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags & ~ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
+               PERROR("Invalid flags.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid timeout specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if ((start_mode != ME_START_MODE_BLOCKING)
+           && (start_mode != ME_START_MODE_NONBLOCKING)) {
+               PERROR("Invalid start mode specified.\n");
+               return ME_ERRNO_INVALID_START_MODE;
+       }
+
+       if (time_out) {
+               delay = (time_out * HZ) / 1000;
+               if (delay == 0)
+                       delay = 1;
+       }
+
+       switch (instance->status) {     //Checking actual mode.
+       case ao_status_stream_configured:
+       case ao_status_stream_end:
+               //Correct modes!
+               break;
+
+               //The device is in wrong mode.
+       case ao_status_none:
+       case ao_status_single_configured:
+       case ao_status_single_run_wait:
+       case ao_status_single_run:
+       case ao_status_single_end_wait:
+               PERROR
+                   ("Subdevice must be preinitialize correctly for streaming.\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+
+       case ao_status_stream_fifo_error:
+       case ao_status_stream_buffer_error:
+       case ao_status_stream_error:
+               PDEBUG("Before restart broke stream 'STOP' must be caled.\n");
+               return ME_STATUS_ERROR;
+
+       case ao_status_stream_run_wait:
+       case ao_status_stream_run:
+       case ao_status_stream_end_wait:
+               PDEBUG("Stream is already working.\n");
+               return ME_ERRNO_SUBDEVICE_BUSY;
+
+       default:
+               instance->status = ao_status_stream_error;
+               PERROR_CRITICAL("Status is in wrong state!\n");
+               return ME_ERRNO_INTERNAL;
+
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->mode == ME6000_AO_CONTINOUS) {    //Continous
+               instance->circ_buf.tail += instance->preloaded_count;
+               instance->circ_buf.tail &= instance->circ_buf.mask;
+       }
+       circ_buffer_count = me_circ_buf_values(&instance->circ_buf);
+
+       if (!circ_buffer_count && !instance->preloaded_count) { //No values in buffer
+               ME_SUBDEVICE_EXIT;
+               PERROR("No values in buffer!\n");
+               return ME_ERRNO_LACK_OF_RESOURCES;
+       }
+
+       //Cancel control task
+       PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
+       instance->ao_control_task_flag = 0;
+       cancel_delayed_work(&instance->ao_control_task);
+
+       //Stop device
+       err = ao_stop_immediately(instance);
+       if (err) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               ME_SUBDEVICE_EXIT;
+
+               return ME_ERRNO_SUBDEVICE_BUSY;
+       }
+       //Set values for single_read()
+       instance->single_value = ME6000_AO_MAX_DATA + 1;
+       instance->single_value_in_fifo = ME6000_AO_MAX_DATA + 1;
+
+       //Setting stop points
+       if (instance->stop_mode == ME6000_AO_SCAN_STOP_MODE) {
+               instance->stop_data_count =
+                   instance->stop_count * circ_buffer_count;
+       } else {
+               instance->stop_data_count = instance->stop_count;
+       }
+
+       if ((instance->stop_data_count != 0)
+           && (instance->stop_data_count < circ_buffer_count)) {
+               PERROR("More data in buffer than previously set limit!\n");
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       ctrl = inl(instance->ctrl_reg);
+       //Check FIFO
+       if (!(ctrl & ME6000_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it. <= This should be done by user call with ME_WRITE_MODE_PRELOAD
+               PINFO("Enableing FIFO.\n");
+               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_FIFO;
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+
+               instance->preloaded_count = 0;
+               instance->data_count = 0;
+       } else {                //Block IRQ
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+       }
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+
+       //Reset interrupt latch
+       inl(instance->irq_reset_reg);
+
+       //Fill FIFO <= Generaly this should be done by user pre-load call but this is second place to do it.
+       status = inl(instance->status_reg);
+       if (!(status & ME6000_AO_STATUS_BIT_EF)) {      //FIFO empty
+               if (instance->stop_data_count != 0) {
+                       count = ME6000_AO_FIFO_COUNT;
+               } else {
+                       count =
+                           (ME6000_AO_FIFO_COUNT <
+                            instance->
+                            stop_data_count) ? ME6000_AO_FIFO_COUNT :
+                           instance->stop_data_count;
+               }
+
+               //Copy data
+               count =
+                   ao_write_data(instance, count, instance->preloaded_count);
+
+               if (count < 0) {        //This should never happend!
+                       PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+       }
+       //Set pre-load features.
+       spin_lock(instance->preload_reg_lock);
+       synch = inl(instance->preload_reg);
+       synch &=
+           ~((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) << instance->
+             ao_idx);
+       synch |=
+           (instance->start_mode & ~ME6000_AO_EXT_TRIG) << instance->ao_idx;
+       outl(synch, instance->preload_reg);
+       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->preload_reg - instance->reg_base, synch);
+       spin_unlock(instance->preload_reg_lock);
+
+       //Default count is '0'
+       if (instance->mode == ME6000_AO_CONTINOUS) {    //Continous
+               instance->preloaded_count = 0;
+               instance->circ_buf.tail += count;
+               instance->circ_buf.tail &= instance->circ_buf.mask;
+       } else {                //Wraparound
+               instance->preloaded_count += count;
+               instance->data_count += count;
+
+               //Special case: Infinite wraparound with less than FIFO datas always should runs in hardware mode.
+               if ((instance->stop_mode == ME6000_AO_INF_STOP_MODE)
+                   && (circ_buffer_count <= ME6000_AO_FIFO_COUNT)) {   //Change to hardware wraparound
+                       PDEBUG
+                           ("Changeing mode from software wraparound to hardware wraparound.\n");
+                       //Copy all data
+                       count =
+                           ao_write_data(instance, circ_buffer_count,
+                                         instance->preloaded_count);
+                       ctrl &= ~ME6000_AO_CTRL_MODE_MASK;
+                       ctrl |= ME6000_AO_MODE_WRAPAROUND;
+               }
+
+               if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) {     //Reset position indicator.
+                       instance->preloaded_count = 0;
+               } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) {       //This should never happend!
+                       PERROR_CRITICAL
+                           ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n");
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+       }
+
+       //Set status to 'wait for start'
+       instance->status = ao_status_stream_run_wait;
+
+       status = inl(instance->status_reg);
+       //Start state machine and interrupts
+       PINFO("<%s:%d> Start state machine.\n", __FUNCTION__, __LINE__);
+       ctrl &= ~(ME6000_AO_CTRL_BIT_STOP | ME6000_AO_CTRL_BIT_IMMEDIATE_STOP);
+       if (instance->start_mode == ME6000_AO_EXT_TRIG) {
+               PDEBUG("DIGITAL TRIGGER\n");
+               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+       }
+       if (!(status & ME6000_AO_STATUS_BIT_HF)) {      //More than half!
+               if ((ctrl & ME6000_AO_CTRL_MODE_MASK) == ME6000_AO_MODE_CONTINUOUS) {   //Enable IRQ only when hardware_continous is set and FIFO is more than half
+                       PINFO("<%s:%d> Start interrupts.\n", __FUNCTION__,
+                             __LINE__);
+                       ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+               }
+       }
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       //Trigger output
+       PINFO("<%s> start mode= 0x%x %s\n", __FUNCTION__, instance->start_mode,
+             (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) ? "SYNCHRONOUS" :
+             "");
+       if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {       //Trigger outputs
+               spin_lock(instance->preload_reg_lock);
+               synch = inl(instance->preload_reg);
+               //Add channel to start list
+               outl(synch | (ME6000_AO_SYNC_HOLD << instance->ao_idx),
+                    instance->preload_reg);
+               PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->preload_reg - instance->reg_base,
+                          synch | (ME6000_AO_SYNC_HOLD << instance->ao_idx));
+
+               //Fire
+               PINFO
+                   ("Fired all software synchronous outputs by software trigger.\n");
+               outl(0x8000, instance->single_reg);
+               PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->single_reg - instance->reg_base, 0x8000);
+
+               //Restore save settings
+               outl(synch, instance->preload_reg);
+               PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->preload_reg - instance->reg_base, synch);
+               spin_unlock(instance->preload_reg_lock);
+       } else if (!instance->start_mode) {     //Trigger outputs
+/*
+               spin_lock(instance->preload_reg_lock);
+                       synch = inl(instance->preload_reg);
+                       //Remove channel from start list
+                       outl(synch & ~(ME6000_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch & ~(ME6000_AO_SYNC_HOLD << instance->ao_idx));
+*/
+               //Fire
+               PINFO("Software trigger.\n");
+               outl(0x8000, instance->single_reg);
+               PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->single_reg - instance->reg_base, 0x8000);
+
+/*
+                       //Restore save settings
+                       outl(synch, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch);
+               spin_unlock(instance->preload_reg_lock);
+*/
+       }
+       // Set control task's timeout
+       instance->timeout.delay = delay;
+       instance->timeout.start_time = jiffies;
+
+       if (status & ME6000_AO_STATUS_BIT_HF) { //Less than half but not empty!
+               PINFO("Less than half.\n");
+               if (instance->stop_data_count == 0) {
+                       count = ME6000_AO_FIFO_COUNT / 2;
+               } else {
+                       count =
+                           ((ME6000_AO_FIFO_COUNT / 2) <
+                            instance->stop_data_count) ? ME6000_AO_FIFO_COUNT /
+                           2 : instance->stop_data_count;
+               }
+
+               //Copy data
+               count =
+                   ao_write_data(instance, count, instance->preloaded_count);
+
+               if (count < 0) {        //This should never happend!
+                       PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+
+               if (instance->mode == ME6000_AO_CONTINOUS) {    //Continous
+                       instance->circ_buf.tail += count;
+                       instance->circ_buf.tail &= instance->circ_buf.mask;
+               } else {        //Wraparound
+                       instance->data_count += count;
+                       instance->preloaded_count += count;
+
+                       if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) {     //Reset position indicator.
+                               instance->preloaded_count = 0;
+                       } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) {       //This should never happend!
+                               PERROR_CRITICAL
+                                   ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n");
+                               ME_SUBDEVICE_EXIT;
+                               return ME_ERRNO_INTERNAL;
+                       }
+               }
+
+               status = inl(instance->status_reg);
+               if (!(status & ME6000_AO_STATUS_BIT_HF)) {      //More than half!
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       PINFO("<%s:%d> Start interrupts.\n", __FUNCTION__,
+                             __LINE__);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+               }
+       }
+       //Special case: Limited wraparound with less than HALF FIFO datas need work around to generate first interrupt.
+       if ((instance->stop_mode != ME6000_AO_INF_STOP_MODE)
+           && (instance->mode == ME6000_AO_SW_WRAP_MODE)
+           && (circ_buffer_count <= (ME6000_AO_FIFO_COUNT / 2))) {     //Put more data to FIFO
+               PINFO("Limited wraparound with less than HALF FIFO datas.\n");
+               if (instance->preloaded_count) {        //This should never happend!
+                       PERROR_CRITICAL
+                           ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n");
+                       ME_SUBDEVICE_EXIT;
+                       return ME_ERRNO_INTERNAL;
+               }
+
+               while (instance->stop_data_count > instance->data_count) {      //Maximum data not set jet.
+                       //Copy to buffer
+                       if (circ_buffer_count != ao_write_data(instance, circ_buffer_count, 0)) {       //This should never happend!
+                               PERROR_CRITICAL
+                                   ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n");
+                               ME_SUBDEVICE_EXIT;
+                               return ME_ERRNO_INTERNAL;
+                       }
+                       instance->data_count += circ_buffer_count;
+
+                       if (!((status = inl(instance->status_reg)) & ME6000_AO_STATUS_BIT_HF)) {        //FIFO is more than half. Enable IRQ and end copy.
+                               //Reset interrupt latch
+                               inl(instance->irq_reset_reg);
+
+                               spin_lock_irqsave(&instance->subdevice_lock,
+                                                 cpu_flags);
+                               PINFO("<%s:%d> Start interrupts.\n",
+                                     __FUNCTION__, __LINE__);
+                               ctrl = inl(instance->ctrl_reg);
+                               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                               outl(ctrl, instance->ctrl_reg);
+                               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->ctrl_reg -
+                                          instance->reg_base, ctrl);
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+                               break;
+                       }
+               }
+       }
+       // Schedule control task
+       instance->ao_control_task_flag = 1;
+       queue_delayed_work(instance->me6000_workqueue,
+                          &instance->ao_control_task, 1);
+
+       if (start_mode == ME_START_MODE_BLOCKING) {     //Wait for start.
+               ref = jiffies;
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_stream_run_wait),
+                                                (delay) ? delay +
+                                                1 : LONG_MAX);
+
+               if ((instance->status != ao_status_stream_run)
+                   && (instance->status != ao_status_stream_end)) {
+                       PDEBUG("Starting stream canceled. %d\n",
+                              instance->status);
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait on start of state machine interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               if ((delay) && ((jiffies - ref) >= delay)) {
+                       if (instance->status != ao_status_stream_run) {
+                               if (instance->status == ao_status_stream_end) {
+                                       PDEBUG("Timeout reached.\n");
+                               } else if ((jiffies - ref) > delay) {
+                                       PERROR
+                                           ("Timeout reached. Not handled by control task!\n");
+                                       ao_stop_immediately(instance);
+                               } else {
+                                       PERROR
+                                           ("Timeout reached. Signal come but status is strange: %d\n",
+                                            instance->status);
+                                       ao_stop_immediately(instance);
+                               }
+
+                               instance->ao_control_task_flag = 0;
+                               cancel_delayed_work(&instance->ao_control_task);
+                               instance->status = ao_status_stream_end;
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               }
+       }
+
+       ME_SUBDEVICE_EXIT;
+       return err;
+}
+
+static int me6000_ao_io_stream_status(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int wait,
+                                     int *status, int *values, int flags)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((wait != ME_WAIT_NONE) && (wait != ME_WAIT_IDLE)) {
+               PERROR("Invalid wait argument specified.\n");
+               *status = ME_STATUS_INVALID;
+               return ME_ERRNO_INVALID_WAIT;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       switch (instance->status) {
+       case ao_status_single_configured:
+       case ao_status_single_end:
+       case ao_status_stream_configured:
+       case ao_status_stream_end:
+       case ao_status_stream_fifo_error:
+       case ao_status_stream_buffer_error:
+       case ao_status_stream_error:
+               *status = ME_STATUS_IDLE;
+               break;
+
+       case ao_status_single_run_wait:
+       case ao_status_single_run:
+       case ao_status_single_end_wait:
+       case ao_status_stream_run_wait:
+       case ao_status_stream_run:
+       case ao_status_stream_end_wait:
+               *status = ME_STATUS_BUSY;
+               break;
+
+       case ao_status_none:
+       default:
+               *status =
+                   (inl(instance->status_reg) & ME6000_AO_STATUS_BIT_FSM) ?
+                   ME_STATUS_BUSY : ME_STATUS_IDLE;
+               break;
+       }
+
+       if ((wait == ME_WAIT_IDLE) && (*status == ME_STATUS_BUSY)) {
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                ((instance->status !=
+                                                  ao_status_single_run_wait)
+                                                 && (instance->status !=
+                                                     ao_status_single_run)
+                                                 && (instance->status !=
+                                                     ao_status_single_end_wait)
+                                                 && (instance->status !=
+                                                     ao_status_stream_run_wait)
+                                                 && (instance->status !=
+                                                     ao_status_stream_run)
+                                                 && (instance->status !=
+                                                     ao_status_stream_end_wait)),
+                                                LONG_MAX);
+
+               if (instance->status != ao_status_stream_end) {
+                       PDEBUG("Wait for IDLE canceled. %d\n",
+                              instance->status);
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Wait for IDLE interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+
+               *status = ME_STATUS_IDLE;
+       }
+
+       *values = me_circ_buf_space(&instance->circ_buf);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_stream_stop(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int stop_mode, int flags)
+{                              /// @note Stop work and empty buffer and FIFO
+       int err = ME_ERRNO_SUCCESS;
+       me6000_ao_subdevice_t *instance;
+       unsigned long cpu_flags;
+       volatile uint32_t ctrl;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (flags & ~ME_IO_STREAM_STOP_PRESERVE_BUFFERS) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if ((stop_mode != ME_STOP_MODE_IMMEDIATE)
+           && (stop_mode != ME_STOP_MODE_LAST_VALUE)) {
+               PERROR("Invalid stop mode specified.\n");
+               return ME_ERRNO_INVALID_STOP_MODE;
+       }
+
+       if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (instance->status < ao_status_stream_configured) {
+               //There is nothing to stop!
+               PERROR("Subdevice not in streaming mode. %d\n",
+                      instance->status);
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       //Mark as stopping. => Software stop.
+       instance->status = ao_status_stream_end_wait;
+
+       if (stop_mode == ME_STOP_MODE_IMMEDIATE) {      //Stopped now!
+               err = ao_stop_immediately(instance);
+       } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) {
+               ctrl = inl(instance->ctrl_reg) & ME6000_AO_CTRL_MODE_MASK;
+               if (ctrl == ME6000_AO_MODE_WRAPAROUND) {        //Hardware wraparound => Hardware stop.
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |= ME6000_AO_CTRL_BIT_STOP;
+                       ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       //Reset interrupt latch
+                       inl(instance->irq_reset_reg);
+               }
+               //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
+               wait_event_interruptible_timeout(instance->wait_queue,
+                                                (instance->status !=
+                                                 ao_status_stream_end_wait),
+                                                LONG_MAX);
+
+               if (instance->status != ao_status_stream_end) {
+                       PDEBUG("Stopping stream canceled.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+
+               if (signal_pending(current)) {
+                       PERROR("Stopping stream interrupted.\n");
+                       instance->status = ao_status_none;
+                       ao_stop_immediately(instance);
+                       err = ME_ERRNO_SIGNAL;
+               }
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       ctrl = inl(instance->ctrl_reg);
+       ctrl |= ME6000_AO_CTRL_BIT_STOP | ME6000_AO_CTRL_BIT_IMMEDIATE_STOP;
+       ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+       if (!flags) {           //Reset FIFO
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_FIFO;
+       }
+       outl(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       //Reset interrupt latch
+       inl(instance->irq_reset_reg);
+
+       if (!flags) {           //Reset software buffer
+               instance->circ_buf.head = 0;
+               instance->circ_buf.tail = 0;
+               instance->preloaded_count = 0;
+               instance->data_count = 0;
+       }
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_ao_io_stream_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int write_mode,
+                                    int *values, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me6000_ao_subdevice_t *instance;
+       unsigned long cpu_flags = 0;
+       uint32_t reg_copy;
+
+       int copied_from_user = 0;
+       int left_to_copy_from_user = *count;
+
+       int copied_values;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       //Checking arguments
+       if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {
+               PERROR("Not a streaming ao.\n");
+               return ME_ERRNO_NOT_SUPPORTED;
+       }
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (*count <= 0) {
+               PERROR("Invalid count of values specified.\n");
+               return ME_ERRNO_INVALID_VALUE_COUNT;
+       }
+
+       if (values == NULL) {
+               PERROR("Invalid address of values specified.\n");
+               return ME_ERRNO_INVALID_POINTER;
+       }
+
+       if ((instance->status == ao_status_none) || (instance->status == ao_status_single_configured)) {        //The device is in single mode.
+               PERROR
+                   ("Subdevice must be preinitialize correctly for streaming.\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+       }
+
+       switch (write_mode) {
+       case ME_WRITE_MODE_PRELOAD:
+
+               //Device must be stopped.
+               if ((instance->status != ao_status_stream_configured)
+                   && (instance->status != ao_status_stream_end)) {
+                       PERROR
+                           ("Subdevice mustn't be runing when 'pre-load' mode is used.\n");
+                       return ME_ERRNO_PREVIOUS_CONFIG;
+               }
+               break;
+       case ME_WRITE_MODE_NONBLOCKING:
+       case ME_WRITE_MODE_BLOCKING:
+               /// @note In blocking mode: When device is not runing and there is not enought space call will blocked up!
+               /// @note Some other thread must empty buffer by strating engine.
+               break;
+
+       default:
+               PERROR("Invalid write mode specified.\n");
+               return ME_ERRNO_INVALID_WRITE_MODE;
+       }
+
+       if (instance->mode & ME6000_AO_WRAP_MODE) {     //Wraparound mode. Device must be stopped.
+               if ((instance->status != ao_status_stream_configured)
+                   && (instance->status != ao_status_stream_end)) {
+                       PERROR
+                           ("Subdevice mustn't be runing when 'pre-load' mode is used.\n");
+                       return ME_ERRNO_INVALID_WRITE_MODE;
+               }
+       }
+
+       if ((instance->mode == ME6000_AO_HW_WRAP_MODE)
+           && (write_mode != ME_WRITE_MODE_PRELOAD)) {
+/*
+               PERROR("Only 'pre-load' write is acceptable in hardware wraparound mode.\n");
+               return ME_ERRNO_PREVIOUS_CONFIG;
+*/
+               //This is transparent for user.
+               PDEBUG("Changing write_mode to ME_WRITE_MODE_PRELOAD.\n");
+               write_mode = ME_WRITE_MODE_PRELOAD;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (write_mode == ME_WRITE_MODE_PRELOAD) {      //Init enviroment - preload
+               spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+               reg_copy = inl(instance->ctrl_reg);
+               //Check FIFO
+               if (!(reg_copy & ME6000_AO_CTRL_BIT_ENABLE_FIFO)) {     //FIFO not active. Enable it.
+                       reg_copy |= ME6000_AO_CTRL_BIT_ENABLE_FIFO;
+                       outl(reg_copy, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  reg_copy);
+                       instance->preloaded_count = 0;
+               }
+               spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       }
+
+       while (1) {
+               //Copy to buffer. This step is common for all modes.
+               copied_from_user =
+                   ao_get_data_from_user(instance, left_to_copy_from_user,
+                                         values + (*count -
+                                                   left_to_copy_from_user));
+               left_to_copy_from_user -= copied_from_user;
+
+               reg_copy = inl(instance->status_reg);
+               if ((instance->status == ao_status_stream_run) && !(reg_copy & ME6000_AO_STATUS_BIT_FSM)) {     //BROKEN PIPE! The state machine is stoped but logical status show that should be working.
+                       PERROR("Broken pipe in write.\n");
+                       err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
+                       break;
+               }
+
+               if ((instance->status == ao_status_stream_run) && (instance->mode == ME6000_AO_CONTINOUS) && (reg_copy & ME6000_AO_STATUS_BIT_HF)) {    //Continous mode runing and data are below half!
+
+                       // Block interrupts.
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       reg_copy = inl(instance->ctrl_reg);
+                       reg_copy &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(reg_copy, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  reg_copy);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       //Fast copy
+                       copied_values =
+                           ao_write_data(instance, ME6000_AO_FIFO_COUNT / 2,
+                                         0);
+                       if (copied_values > 0) {
+                               instance->circ_buf.tail += copied_values;
+                               instance->circ_buf.tail &=
+                                   instance->circ_buf.mask;
+                               continue;
+                       }
+                       //Reset interrupt latch
+                       inl(instance->irq_reset_reg);
+
+                       // Activate interrupts.
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       reg_copy = inl(instance->ctrl_reg);
+                       reg_copy |= ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       outl(reg_copy, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  reg_copy);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (copied_values == 0) {       //This was checked and never should happend!
+                               PERROR_CRITICAL("COPY FINISH WITH 0!\n");
+                       }
+
+                       if (copied_values < 0) {        //This was checked and never should happend!
+                               PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
+                               instance->status = ao_status_stream_fifo_error;
+                               err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+                               break;
+                       }
+               }
+
+               if (!left_to_copy_from_user) {  //All datas were copied.
+                       break;
+               } else {        //Not all datas were copied.
+                       if (instance->mode & ME6000_AO_WRAP_MODE) {     //Error too much datas! Wraparound is limited in size!
+                               PERROR
+                                   ("Too much data for wraparound mode!  Exceeded size of %ld.\n",
+                                    ME6000_AO_CIRC_BUF_COUNT - 1);
+                               err = ME_ERRNO_RING_BUFFER_OVERFLOW;
+                               break;
+                       }
+
+                       if (write_mode != ME_WRITE_MODE_BLOCKING) {     //Non blocking calls
+                               break;
+                       }
+
+                       wait_event_interruptible(instance->wait_queue,
+                                                me_circ_buf_space(&instance->
+                                                                  circ_buf));
+
+                       if (signal_pending(current)) {
+                               PERROR("Writing interrupted by signal.\n");
+                               instance->status = ao_status_none;
+                               ao_stop_immediately(instance);
+                               err = ME_ERRNO_SIGNAL;
+                               break;
+                       }
+
+                       if (instance->status == ao_status_none) {       //Reset
+                               PERROR("Writing interrupted by reset.\n");
+                               err = ME_ERRNO_CANCELLED;
+                               break;
+                       }
+               }
+       }
+
+       if (write_mode == ME_WRITE_MODE_PRELOAD) {      //Copy data to FIFO - preload
+               copied_values =
+                   ao_write_data_pooling(instance, ME6000_AO_FIFO_COUNT,
+                                         instance->preloaded_count);
+               instance->preloaded_count += copied_values;
+               instance->data_count += copied_values;
+
+               if ((instance->mode == ME6000_AO_HW_WRAP_MODE)
+                   && (me_circ_buf_values(&instance->circ_buf) >
+                       ME6000_AO_FIFO_COUNT)) {
+                       PERROR
+                           ("Too much data for hardware wraparound mode! Exceeded size of %d.\n",
+                            ME6000_AO_FIFO_COUNT);
+                       err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+               }
+       }
+
+       *count = *count - left_to_copy_from_user;
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me6000_ao_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me6000_ao_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       me6000_ao_subdevice_t *instance = dev_id;
+       uint32_t irq_status;
+       uint32_t ctrl;
+       uint32_t status;
+       int count = 0;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       irq_status = inl(instance->irq_status_reg);
+       if (!(irq_status & (ME6000_IRQ_STATUS_BIT_AO_HF << instance->ao_idx))) {
+               PINFO("%ld Shared interrupt. %s(): ID=%d: status_reg=0x%04X\n",
+                     jiffies, __FUNCTION__, instance->ao_idx, irq_status);
+               return IRQ_NONE;
+       }
+
+       if (!instance->circ_buf.buf) {
+               instance->status = ao_status_stream_error;
+               PERROR_CRITICAL("CIRCULAR BUFFER NOT EXISTS!\n");
+               //Block interrupts. Stop machine.
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+               ctrl |=
+                   ME6000_AO_CTRL_BIT_IMMEDIATE_STOP | ME6000_AO_CTRL_BIT_STOP;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               //Inform user
+               wake_up_interruptible_all(&instance->wait_queue);
+               return IRQ_HANDLED;
+       }
+
+       status = inl(instance->status_reg);
+       if (!(status & ME6000_AO_STATUS_BIT_FSM)) {     //Too late. Not working! END? BROKEN PIPE?
+               /// @note Error checking was moved to separate task.
+               PDEBUG("Interrupt come but ISM is not working!\n");
+               //Block interrupts. Stop machine.
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+               ctrl |=
+                   ME6000_AO_CTRL_BIT_STOP | ME6000_AO_CTRL_BIT_IMMEDIATE_STOP;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               //Reset interrupt latch
+               inl(instance->irq_reset_reg);
+
+               /// @note User notification was also moved to separate task.
+               return IRQ_HANDLED;
+       }
+       //General procedure. Process more datas.
+
+#ifdef MEDEBUG_DEBUG
+       if (!me_circ_buf_values(&instance->circ_buf)) { //Buffer is empty!
+               PDEBUG("Circular buffer empty!\n");
+       }
+#endif
+
+       //Check FIFO
+       if (status & ME6000_AO_STATUS_BIT_HF) { //OK less than half
+
+               //Block interrupts
+               ctrl = inl(instance->ctrl_reg);
+               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+
+               do {
+                       //Calculate how many should be copied.
+                       count =
+                           (instance->stop_data_count) ? instance->
+                           stop_data_count -
+                           instance->data_count : ME6000_AO_FIFO_COUNT / 2;
+                       if (ME6000_AO_FIFO_COUNT / 2 < count) {
+                               count = ME6000_AO_FIFO_COUNT / 2;
+                       }
+                       //Copy data
+                       if (instance->mode == ME6000_AO_CONTINOUS) {    //Continous
+                               count = ao_write_data(instance, count, 0);
+                               if (count > 0) {
+                                       instance->circ_buf.tail += count;
+                                       instance->circ_buf.tail &=
+                                           instance->circ_buf.mask;
+                                       instance->data_count += count;
+
+                                       if ((instance->status == ao_status_stream_end_wait) && !me_circ_buf_values(&instance->circ_buf)) {      //Stoping. Whole buffer was copied.
+                                               break;
+                                       }
+                               }
+                       } else if ((instance->mode == ME6000_AO_SW_WRAP_MODE) && ((ctrl & ME6000_AO_CTRL_MODE_MASK) == ME6000_AO_MODE_CONTINUOUS)) {    //Wraparound (software)
+                               if (instance->status == ao_status_stream_end_wait) {    //We stoping => Copy to the end of the buffer.
+                                       count =
+                                           ao_write_data(instance, count, 0);
+                               } else {        //Copy in wraparound mode.
+                                       count =
+                                           ao_write_data_wraparound(instance,
+                                                                    count,
+                                                                    instance->
+                                                                    preloaded_count);
+                               }
+
+                               if (count > 0) {
+                                       instance->data_count += count;
+                                       instance->preloaded_count += count;
+                                       instance->preloaded_count %=
+                                           me_circ_buf_values(&instance->
+                                                              circ_buf);
+
+                                       if ((instance->status == ao_status_stream_end_wait) && !instance->preloaded_count) {    //Stoping. Whole buffer was copied.
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if ((count <= 0) || (instance->stop_data_count && (instance->stop_data_count <= instance->data_count))) {       //End of work.
+                               break;
+                       }
+               }               //Repeat if still is under half fifo
+               while ((status =
+                       inl(instance->status_reg)) & ME6000_AO_STATUS_BIT_HF);
+
+               //Unblock interrupts
+               ctrl = inl(instance->ctrl_reg);
+               if (count >= 0) {       //Copy was successful.
+                       if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) { //Finishing work. No more interrupts.
+                               PDEBUG("Finishing work. Interrupt disabled.\n");
+                               instance->status = ao_status_stream_end_wait;
+                       } else if (count > 0) { //Normal work. Enable interrupt.
+                               PDEBUG("Normal work. Enable interrupt.\n");
+                               ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ;
+                       } else {        //Normal work but there are no more data in buffer. Interrupt blocked. stream_write() will unblock it.
+                               PDEBUG
+                                   ("No data in software buffer. Interrupt blocked.\n");
+                       }
+               } else {        //Error during copy.
+                       instance->status = ao_status_stream_fifo_error;
+               }
+
+               outl(ctrl, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, ctrl);
+       } else {                //?? more than half
+               PDEBUG
+                   ("Interrupt come but FIFO more than half full! Reset interrupt.\n");
+       }
+
+       PINFO("ISR: Buffer count: %d.(T:%d H:%d)\n",
+             me_circ_buf_values(&instance->circ_buf), instance->circ_buf.tail,
+             instance->circ_buf.head);
+       PINFO("ISR: Stop count: %d.\n", instance->stop_count);
+       PINFO("ISR: Stop data count: %d.\n", instance->stop_data_count);
+       PINFO("ISR: Data count: %d.\n", instance->data_count);
+
+       //Reset interrupt latch
+       inl(instance->irq_reset_reg);
+
+       //Inform user
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+static void me6000_ao_destructor(struct me_subdevice *subdevice)
+{
+       me6000_ao_subdevice_t *instance;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       instance->ao_control_task_flag = 0;
+
+       // Reset subdevice to asure clean exit.
+       me6000_ao_io_reset_subdevice(subdevice, NULL,
+                                    ME_IO_RESET_SUBDEVICE_NO_FLAGS);
+
+       // Remove any tasks from work queue. This is paranoic because it was done allready in reset().
+       if (!cancel_delayed_work(&instance->ao_control_task)) { //Wait 2 ticks to be sure that control task is removed from queue.
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(2);
+       }
+
+       if (instance->fifo & ME6000_AO_HAS_FIFO) {
+               if (instance->irq) {
+                       free_irq(instance->irq, instance);
+                       instance->irq = 0;
+               }
+
+               if (instance->circ_buf.buf) {
+                       PDEBUG("free circ_buf = %p size=%d",
+                              instance->circ_buf.buf,
+                              PAGE_SHIFT << ME6000_AO_CIRC_BUF_SIZE_ORDER);
+                       free_pages((unsigned long)instance->circ_buf.buf,
+                                  ME6000_AO_CIRC_BUF_SIZE_ORDER);
+               }
+               instance->circ_buf.buf = NULL;
+       }
+
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+me6000_ao_subdevice_t *me6000_ao_constructor(uint32_t reg_base,
+                                            spinlock_t * preload_reg_lock,
+                                            uint32_t * preload_flags,
+                                            uint32_t * triggering_flags,
+                                            int ao_idx,
+                                            int fifo,
+                                            int irq,
+                                            int high_range,
+                                            struct workqueue_struct *me6000_wq)
+{
+       me6000_ao_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed ID=%d.\n", ao_idx);
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me6000_ao_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me6000_ao_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->preload_reg_lock = preload_reg_lock;
+       subdevice->preload_flags = preload_flags;
+       subdevice->triggering_flags = triggering_flags;
+
+       /* Store analog output index */
+       subdevice->ao_idx = ao_idx;
+
+       /* Store if analog output has fifo */
+       subdevice->fifo = fifo;
+
+       if (subdevice->fifo & ME6000_AO_HAS_FIFO) {
+               /* Allocate and initialize circular buffer */
+               subdevice->circ_buf.mask = ME6000_AO_CIRC_BUF_COUNT - 1;
+               subdevice->circ_buf.buf =
+                   (void *)__get_free_pages(GFP_KERNEL,
+                                            ME6000_AO_CIRC_BUF_SIZE_ORDER);
+               PDEBUG("circ_buf = %p size=%ld\n", subdevice->circ_buf.buf,
+                      ME6000_AO_CIRC_BUF_SIZE);
+
+               if (!subdevice->circ_buf.buf) {
+                       PERROR
+                           ("Cannot initialize subdevice base class instance.\n");
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+               memset(subdevice->circ_buf.buf, 0, ME6000_AO_CIRC_BUF_SIZE);
+       } else {
+               subdevice->circ_buf.mask = 0;
+               subdevice->circ_buf.buf = NULL;
+       }
+       subdevice->circ_buf.head = 0;
+       subdevice->circ_buf.tail = 0;
+
+       subdevice->status = ao_status_none;
+       subdevice->ao_control_task_flag = 0;
+       subdevice->timeout.delay = 0;
+       subdevice->timeout.start_time = jiffies;
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       /* Initialize single value to 0V */
+       subdevice->single_value = 0x8000;
+       subdevice->single_value_in_fifo = 0x8000;
+
+       /* Initialize range boarders */
+       if (high_range) {
+               subdevice->min = ME6000_AO_MIN_RANGE_HIGH;
+               subdevice->max = ME6000_AO_MAX_RANGE_HIGH;
+       } else {
+               subdevice->min = ME6000_AO_MIN_RANGE;
+               subdevice->max = ME6000_AO_MAX_RANGE;
+       }
+
+       /* Register interrupt service routine */
+
+       if (subdevice->fifo & ME6000_AO_HAS_FIFO) {
+               subdevice->irq = irq;
+               if (request_irq(subdevice->irq, me6000_ao_isr,
+#ifdef IRQF_DISABLED
+                               IRQF_DISABLED | IRQF_SHARED,
+#else
+                               SA_INTERRUPT | SA_SHIRQ,
+#endif
+                               ME6000_NAME, subdevice)) {
+                       PERROR("Cannot get interrupt line.\n");
+                       PDEBUG("free circ_buf = %p size=%d",
+                              subdevice->circ_buf.buf,
+                              PAGE_SHIFT << ME6000_AO_CIRC_BUF_SIZE_ORDER);
+                       free_pages((unsigned long)subdevice->circ_buf.buf,
+                                  ME6000_AO_CIRC_BUF_SIZE_ORDER);
+                       subdevice->circ_buf.buf = NULL;
+                       kfree(subdevice);
+                       return NULL;
+               }
+               PINFO("Registered irq=%d.\n", subdevice->irq);
+       } else {
+               subdevice->irq = 0;
+       }
+
+       /* Initialize registers */
+       // Only streamed subdevices support interrupts. For the rest this register has no meaning.
+       subdevice->irq_status_reg = reg_base + ME6000_AO_IRQ_STATUS_REG;
+       subdevice->preload_reg = reg_base + ME6000_AO_PRELOAD_REG;
+
+       if (ao_idx == 0) {
+               subdevice->ctrl_reg = reg_base + ME6000_AO_00_CTRL_REG;
+               subdevice->status_reg = reg_base + ME6000_AO_00_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME6000_AO_00_FIFO_REG;
+               subdevice->timer_reg = reg_base + ME6000_AO_00_TIMER_REG;
+               subdevice->irq_reset_reg =
+                   reg_base + ME6000_AO_00_IRQ_RESET_REG;
+               subdevice->single_reg = reg_base + ME6000_AO_00_SINGLE_REG;
+       } else if (ao_idx == 1) {
+               subdevice->ctrl_reg = reg_base + ME6000_AO_01_CTRL_REG;
+               subdevice->status_reg = reg_base + ME6000_AO_01_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME6000_AO_01_FIFO_REG;
+               subdevice->timer_reg = reg_base + ME6000_AO_01_TIMER_REG;
+               subdevice->irq_reset_reg =
+                   reg_base + ME6000_AO_01_IRQ_RESET_REG;
+               subdevice->single_reg = reg_base + ME6000_AO_01_SINGLE_REG;
+       } else if (ao_idx == 2) {
+               subdevice->ctrl_reg = reg_base + ME6000_AO_02_CTRL_REG;
+               subdevice->status_reg = reg_base + ME6000_AO_02_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME6000_AO_02_FIFO_REG;
+               subdevice->timer_reg = reg_base + ME6000_AO_02_TIMER_REG;
+               subdevice->irq_reset_reg =
+                   reg_base + ME6000_AO_02_IRQ_RESET_REG;
+               subdevice->single_reg = reg_base + ME6000_AO_02_SINGLE_REG;
+       } else if (ao_idx == 3) {
+               subdevice->ctrl_reg = reg_base + ME6000_AO_03_CTRL_REG;
+               subdevice->status_reg = reg_base + ME6000_AO_03_STATUS_REG;
+               subdevice->fifo_reg = reg_base + ME6000_AO_03_FIFO_REG;
+               subdevice->timer_reg = reg_base + ME6000_AO_03_TIMER_REG;
+               subdevice->irq_reset_reg =
+                   reg_base + ME6000_AO_03_IRQ_RESET_REG;
+               subdevice->single_reg = reg_base + ME6000_AO_03_SINGLE_REG;
+       } else {
+               subdevice->ctrl_reg = reg_base + ME6000_AO_DUMY;
+               subdevice->fifo_reg = reg_base + ME6000_AO_DUMY;
+               subdevice->timer_reg = reg_base + ME6000_AO_DUMY;
+               subdevice->irq_reset_reg = reg_base + ME6000_AO_DUMY;
+               subdevice->single_reg = reg_base + ME6000_AO_DUMY;
+
+               subdevice->status_reg = reg_base + ME6000_AO_SINGLE_STATUS_REG;
+               if (ao_idx == 4) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_04_SINGLE_REG;
+               } else if (ao_idx == 5) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_05_SINGLE_REG;
+               } else if (ao_idx == 6) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_06_SINGLE_REG;
+               } else if (ao_idx == 7) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_07_SINGLE_REG;
+               } else if (ao_idx == 8) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_08_SINGLE_REG;
+               } else if (ao_idx == 9) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_09_SINGLE_REG;
+               } else if (ao_idx == 10) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_10_SINGLE_REG;
+               } else if (ao_idx == 11) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_11_SINGLE_REG;
+               } else if (ao_idx == 12) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_12_SINGLE_REG;
+               } else if (ao_idx == 13) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_13_SINGLE_REG;
+               } else if (ao_idx == 14) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_14_SINGLE_REG;
+               } else if (ao_idx == 15) {
+                       subdevice->single_reg =
+                           reg_base + ME6000_AO_15_SINGLE_REG;
+               } else {
+                       PERROR_CRITICAL("WRONG SUBDEVICE ID=%d!", ao_idx);
+                       me_subdevice_deinit((me_subdevice_t *) subdevice);
+                       if (subdevice->fifo) {
+                               free_pages((unsigned long)subdevice->circ_buf.
+                                          buf, ME6000_AO_CIRC_BUF_SIZE_ORDER);
+                       }
+                       subdevice->circ_buf.buf = NULL;
+                       kfree(subdevice);
+                       return NULL;
+               }
+       }
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Override base class methods. */
+       subdevice->base.me_subdevice_destructor = me6000_ao_destructor;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me6000_ao_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me6000_ao_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me6000_ao_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me6000_ao_io_single_write;
+       subdevice->base.me_subdevice_io_stream_config =
+           me6000_ao_io_stream_config;
+       subdevice->base.me_subdevice_io_stream_new_values =
+           me6000_ao_io_stream_new_values;
+       subdevice->base.me_subdevice_io_stream_write =
+           me6000_ao_io_stream_write;
+       subdevice->base.me_subdevice_io_stream_start =
+           me6000_ao_io_stream_start;
+       subdevice->base.me_subdevice_io_stream_status =
+           me6000_ao_io_stream_status;
+       subdevice->base.me_subdevice_io_stream_stop = me6000_ao_io_stream_stop;
+       subdevice->base.me_subdevice_query_number_channels =
+           me6000_ao_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me6000_ao_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me6000_ao_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_subdevice_caps_args =
+           me6000_ao_query_subdevice_caps_args;
+       subdevice->base.me_subdevice_query_range_by_min_max =
+           me6000_ao_query_range_by_min_max;
+       subdevice->base.me_subdevice_query_number_ranges =
+           me6000_ao_query_number_ranges;
+       subdevice->base.me_subdevice_query_range_info =
+           me6000_ao_query_range_info;
+       subdevice->base.me_subdevice_query_timer = me6000_ao_query_timer;
+
+       //prepare work queue and work function
+       subdevice->me6000_workqueue = me6000_wq;
+
+/* workqueue API changed in kernel 2.6.20 */
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) )
+       INIT_WORK(&subdevice->ao_control_task, me6000_ao_work_control_task,
+                 (void *)subdevice);
+#else
+       INIT_DELAYED_WORK(&subdevice->ao_control_task,
+                         me6000_ao_work_control_task);
+#endif
+
+       if (subdevice->fifo) {  //Set speed
+               outl(ME6000_AO_MIN_CHAN_TICKS - 1, subdevice->timer_reg);
+               subdevice->hardware_stop_delay = HZ / 10;       //100ms
+       }
+
+       return subdevice;
+}
+
+/** @brief Stop presentation. Preserve FIFOs.
+*
+* @param instance The subdevice instance (pointer).
+*/
+int inline ao_stop_immediately(me6000_ao_subdevice_t * instance)
+{
+       unsigned long cpu_flags;
+       uint32_t ctrl;
+       int timeout;
+       int i;
+       uint32_t single_mask;
+
+       single_mask =
+           (instance->ao_idx - ME6000_AO_SINGLE_STATUS_OFFSET <
+            0) ? 0x0000 : (0x0001 << (instance->ao_idx -
+                                      ME6000_AO_SINGLE_STATUS_OFFSET));
+
+       timeout =
+           (instance->hardware_stop_delay >
+            (HZ / 10)) ? instance->hardware_stop_delay : HZ / 10;
+       for (i = 0; i <= timeout; i++) {
+               if (instance->fifo) {
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched!
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           ME6000_AO_CTRL_BIT_STOP |
+                           ME6000_AO_CTRL_BIT_IMMEDIATE_STOP;
+                       ctrl &=
+                           ~(ME6000_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       if (!(inl(instance->status_reg) & ME6000_AO_STATUS_BIT_FSM)) {  // Exit.
+                               break;
+                       }
+               } else {
+                       if (!(inl(instance->status_reg) & single_mask)) {       // Exit.
+                               break;
+                       }
+               }
+
+               PINFO("<%s> Wait for stop: %d\n", __FUNCTION__, i);
+
+               //Still working!
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(1);
+       }
+
+       if (i > timeout) {
+               PERROR_CRITICAL("FSM IS BUSY!\n");
+               return ME_ERRNO_INTERNAL;
+       }
+       return ME_ERRNO_SUCCESS;
+}
+
+/** @brief Copy data from circular buffer to fifo (fast) in wraparound.
+* @note This is time critical function. Checking is done at begining and end only.
+* @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly.
+*
+* @param instance The subdevice instance (pointer).
+* @param count Maximum number of copied data.
+* @param start_pos Position of the firs value in buffer.
+*
+* @return On success: Number of copied data.
+* @return On error/success: 0. No datas were copied => no data in buffer.
+* @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW.
+*/
+int inline ao_write_data_wraparound(me6000_ao_subdevice_t * instance, int count,
+                                   int start_pos)
+{                              /// @note This is time critical function!
+       uint32_t status;
+       uint32_t value;
+       int pos =
+           (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
+       int local_count = count;
+       int i = 1;
+
+       if (count <= 0) {       //Wrong count!
+               return 0;
+       }
+
+       while (i < local_count) {
+               //Get value from buffer
+               value = *(instance->circ_buf.buf + pos);
+               //Prepare it
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+
+               pos++;
+               pos &= instance->circ_buf.mask;
+               if (pos == instance->circ_buf.head) {
+                       pos = instance->circ_buf.tail;
+               }
+               i++;
+       }
+
+       status = inl(instance->status_reg);
+       if (!(status & ME6000_AO_STATUS_BIT_FF)) {      //FIFO is full before all datas were copied!
+               PERROR("idx=%d FIFO is full before all datas were copied!\n",
+                      instance->ao_idx);
+               return -ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+       } else {                //Add last value
+               value = *(instance->circ_buf.buf + pos);
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+       }
+
+       PINFO("idx=%d WRAPAROUND LOADED %d values\n", instance->ao_idx,
+             local_count);
+       return local_count;
+}
+
+/** @brief Copy data from software buffer to fifo (fast).
+* @note This is time critical function. Checking is done at begining and end only.
+* @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly.
+*
+* @param instance The subdevice instance (pointer).
+* @param count Maximum number of copied data.
+* @param start_pos Position of the firs value in buffer.
+*
+* @return On success: Number of copied data.
+* @return On error/success: 0. No datas were copied => no data in buffer.
+* @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW.
+*/
+int inline ao_write_data(me6000_ao_subdevice_t * instance, int count,
+                        int start_pos)
+{                              /// @note This is time critical function!
+       uint32_t status;
+       uint32_t value;
+       int pos =
+           (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
+       int local_count = count;
+       int max_count;
+       int i = 1;
+
+       if (count <= 0) {       //Wrong count!
+               return 0;
+       }
+
+       max_count = me_circ_buf_values(&instance->circ_buf) - start_pos;
+       if (max_count <= 0) {   //No data to copy!
+               return 0;
+       }
+
+       if (max_count < count) {
+               local_count = max_count;
+       }
+
+       while (i < local_count) {
+               //Get value from buffer
+               value = *(instance->circ_buf.buf + pos);
+               //Prepare it
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+
+               pos++;
+               pos &= instance->circ_buf.mask;
+               i++;
+       }
+
+       status = inl(instance->status_reg);
+       if (!(status & ME6000_AO_STATUS_BIT_FF)) {      //FIFO is full before all datas were copied!
+               PERROR("idx=%d FIFO is full before all datas were copied!\n",
+                      instance->ao_idx);
+               return -ME_ERRNO_FIFO_BUFFER_OVERFLOW;
+       } else {                //Add last value
+               value = *(instance->circ_buf.buf + pos);
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+       }
+
+       PINFO("idx=%d FAST LOADED %d values\n", instance->ao_idx, local_count);
+       return local_count;
+}
+
+/** @brief Copy data from software buffer to fifo (slow).
+* @note This is slow function that copy all data from buffer to FIFO with full control.
+*
+* @param instance The subdevice instance (pointer).
+* @param count Maximum number of copied data.
+* @param start_pos Position of the firs value in buffer.
+*
+* @return On success: Number of copied values.
+* @return On error/success: 0. FIFO was full at begining.
+* @return On error: -ME_ERRNO_RING_BUFFER_UNDEFFLOW.
+*/
+int inline ao_write_data_pooling(me6000_ao_subdevice_t * instance, int count,
+                                int start_pos)
+{                              /// @note This is slow function!
+       uint32_t status;
+       uint32_t value;
+       int pos =
+           (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
+       int local_count = count;
+       int i;
+       int max_count;
+
+       if (count <= 0) {       //Wrong count!
+               PERROR("idx=%d SLOW LOADED: Wrong count!\n", instance->ao_idx);
+               return 0;
+       }
+
+       max_count = me_circ_buf_values(&instance->circ_buf) - start_pos;
+       if (max_count <= 0) {   //No data to copy!
+               PERROR("idx=%d SLOW LOADED: No data to copy!\n",
+                      instance->ao_idx);
+               return 0;
+       }
+
+       if (max_count < count) {
+               local_count = max_count;
+       }
+
+       for (i = 0; i < local_count; i++) {
+               status = inl(instance->status_reg);
+               if (!(status & ME6000_AO_STATUS_BIT_FF)) {      //FIFO is full!
+                       return i;
+               }
+               //Get value from buffer
+               value = *(instance->circ_buf.buf + pos);
+               //Prepare it
+               if (instance->ao_idx & 0x1) {
+                       value <<= 16;
+               }
+               //Put value to FIFO
+               outl(value, instance->fifo_reg);
+               //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
+
+               pos++;
+               pos &= instance->circ_buf.mask;
+       }
+
+       PINFO("idx=%d SLOW LOADED %d values\n", instance->ao_idx, local_count);
+       return local_count;
+}
+
+/** @brief Copy data from user space to circular buffer.
+* @param instance The subdevice instance (pointer).
+* @param count Number of datas in user space.
+* @param user_values Buffer's pointer.
+*
+* @return On success: Number of copied values.
+* @return On error: -ME_ERRNO_INTERNAL.
+*/
+int inline ao_get_data_from_user(me6000_ao_subdevice_t * instance, int count,
+                                int *user_values)
+{
+       int i, err;
+       int empty_space;
+       int copied;
+       int value;
+
+       empty_space = me_circ_buf_space(&instance->circ_buf);
+       //We have only this space free.
+       copied = (count < empty_space) ? count : empty_space;
+       for (i = 0; i < copied; i++) {  //Copy from user to buffer
+               if ((err = get_user(value, (int *)(user_values + i)))) {
+                       PERROR
+                           ("idx=%d BUFFER LOADED: get_user(0x%p) return an error: %d\n",
+                            instance->ao_idx, user_values + i, err);
+                       return -ME_ERRNO_INTERNAL;
+               }
+               /// @note The analog output in me6000 series has size of 16 bits.
+               *(instance->circ_buf.buf + instance->circ_buf.head) =
+                   (uint16_t) value;
+               instance->circ_buf.head++;
+               instance->circ_buf.head &= instance->circ_buf.mask;
+       }
+
+       PINFO("idx=%d BUFFER LOADED %d values\n", instance->ao_idx, copied);
+       return copied;
+}
+
+static void me6000_ao_work_control_task(
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+                                              void *subdevice
+#else
+                                              struct work_struct *work
+#endif
+    )
+{
+       me6000_ao_subdevice_t *instance;
+       unsigned long cpu_flags = 0;
+       uint32_t status;
+       uint32_t ctrl;
+       uint32_t synch;
+       int reschedule = 0;
+       int signaling = 0;
+       uint32_t single_mask;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       instance = (me6000_ao_subdevice_t *) subdevice;
+#else
+       instance =
+           container_of((void *)work, me6000_ao_subdevice_t, ao_control_task);
+#endif
+       PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies,
+             instance->ao_idx);
+
+       status = inl(instance->status_reg);
+       PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->status_reg - instance->reg_base, status);
+
+/// @note AO_STATUS_BIT_FSM doesn't work as should be for pure single channels (idx>=4)
+//      single_mask = (instance->ao_idx-ME6000_AO_SINGLE_STATUS_OFFSET < 0) ? 0x0000 : (0x0001 << (instance->ao_idx-ME6000_AO_SINGLE_STATUS_OFFSET));
+       single_mask = *instance->triggering_flags & (0x1 << instance->ao_idx);
+
+       switch (instance->status) {     // Checking actual mode.
+
+               // Not configured for work.
+       case ao_status_none:
+               break;
+
+               //This are stable modes. No need to do anything. (?)
+       case ao_status_single_configured:
+       case ao_status_stream_configured:
+       case ao_status_stream_fifo_error:
+       case ao_status_stream_buffer_error:
+       case ao_status_stream_error:
+               PERROR("Shouldn't be running!.\n");
+               break;
+
+               // Single modes
+       case ao_status_single_run_wait:
+       case ao_status_single_run:
+       case ao_status_single_end_wait:
+               if (instance->fifo) {   // Extra registers.
+                       if (!(status & ME6000_AO_STATUS_BIT_FSM)) {     // State machine is not working.
+                               if (((instance->fifo & ME6000_AO_HAS_FIFO)
+                                    && (!(status & ME6000_AO_STATUS_BIT_EF)))
+                                   || (!(instance->fifo & ME6000_AO_HAS_FIFO))) {      // Single is in end state.
+                                       PDEBUG
+                                           ("Single call has been complited.\n");
+
+                                       // Set correct value for single_read();
+                                       instance->single_value =
+                                           instance->single_value_in_fifo;
+
+                                       // Set status as 'ao_status_single_end'
+                                       instance->status = ao_status_single_end;
+
+                                       spin_lock(instance->preload_reg_lock);
+                                       if ((single_mask) && (*instance->preload_flags & (ME6000_AO_SYNC_HOLD << instance->ao_idx))) {  // This is one of synchronous start channels. Set all as triggered.
+                                               *instance->triggering_flags =
+                                                   0x00000000;
+                                       } else {
+                                               //Set this channel as triggered (none active).
+                                               *instance->triggering_flags &=
+                                                   ~(0x1 << instance->ao_idx);
+                                       }
+                                       spin_unlock(instance->preload_reg_lock);
+
+                                       // Signal the end.
+                                       signaling = 1;
+                                       // Wait for stop ISM.
+                                       reschedule = 1;
+
+                                       break;
+                               }
+                       }
+                       // Check timeout.
+                       if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {       // Timeout
+                               PDEBUG("Timeout reached.\n");
+                               // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched!
+                               spin_lock_irqsave(&instance->subdevice_lock,
+                                                 cpu_flags);
+                               ctrl = inl(instance->ctrl_reg);
+                               ctrl |=
+                                   ME6000_AO_CTRL_BIT_STOP |
+                                   ME6000_AO_CTRL_BIT_IMMEDIATE_STOP;
+                               ctrl &=
+                                   ~(ME6000_AO_CTRL_BIT_ENABLE_IRQ |
+                                     ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                               ctrl &=
+                                   ~(ME6000_AO_CTRL_BIT_EX_TRIG_EDGE |
+                                     ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
+                               //Disabling FIFO
+                               ctrl &= ~ME6000_AO_CTRL_BIT_ENABLE_FIFO;
+
+                               outl(ctrl, instance->ctrl_reg);
+                               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->ctrl_reg -
+                                          instance->reg_base, ctrl);
+                               spin_unlock_irqrestore(&instance->
+                                                      subdevice_lock,
+                                                      cpu_flags);
+
+                               //Reset interrupt latch
+                               inl(instance->irq_reset_reg);
+
+                               spin_lock(instance->preload_reg_lock);
+                               //Remove from synchronous start. Block triggering from this output.
+                               synch = inl(instance->preload_reg);
+                               synch &=
+                                   ~((ME6000_AO_SYNC_HOLD |
+                                      ME6000_AO_SYNC_EXT_TRIG) << instance->
+                                     ao_idx);
+                               if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {   // No FIFO - set to single safe mode
+                                       synch |=
+                                           ME6000_AO_SYNC_HOLD << instance->
+                                           ao_idx;
+                               }
+                               outl(synch, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    synch);
+                               //Set this channel as triggered (none active).
+                               *instance->triggering_flags &=
+                                   ~(0x1 << instance->ao_idx);
+                               spin_unlock(instance->preload_reg_lock);
+
+                               // Set correct value for single_read();
+                               instance->single_value_in_fifo =
+                                   instance->single_value;
+
+                               instance->status = ao_status_single_end;
+
+                               // Signal the end.
+                               signaling = 1;
+                       }
+               } else {        // No extra registers.
+/*
+                               if (!(status & single_mask))
+                               {// State machine is not working.
+                                       PDEBUG("Single call has been complited.\n");
+
+                                       // Set correct value for single_read();
+                                       instance->single_value = instance->single_value_in_fifo;
+
+                                       // Set status as 'ao_status_single_end'
+                                       instance->status = ao_status_single_end;
+
+                                       // Signal the end.
+                                       signaling = 1;
+                                       // Wait for stop ISM.
+                                       reschedule = 1;
+
+                                       break;
+                               }
+*/
+                       if (!single_mask) {     // Was triggered.
+                               PDEBUG("Single call has been complited.\n");
+
+                               // Set correct value for single_read();
+                               instance->single_value =
+                                   instance->single_value_in_fifo;
+
+                               // Set status as 'ao_status_single_end'
+                               instance->status = ao_status_single_end;
+
+                               // Signal the end.
+                               signaling = 1;
+
+                               break;
+                       }
+                       // Check timeout.
+                       if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {       // Timeout
+                               PDEBUG("Timeout reached.\n");
+
+                               spin_lock(instance->preload_reg_lock);
+                               //Remove from synchronous start. Block triggering from this output.
+                               synch = inl(instance->preload_reg);
+                               synch &=
+                                   ~(ME6000_AO_SYNC_EXT_TRIG << instance->
+                                     ao_idx);
+                               synch |=
+                                   ME6000_AO_SYNC_HOLD << instance->ao_idx;
+
+                               outl(synch, instance->preload_reg);
+                               PDEBUG_REG
+                                   ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->preload_reg - instance->reg_base,
+                                    synch);
+                               //Set this channel as triggered (none active).
+                               *instance->triggering_flags &=
+                                   ~(0x1 << instance->ao_idx);
+                               spin_unlock(instance->preload_reg_lock);
+
+                               // Restore old settings.
+                               PDEBUG("Write old value back to register.\n");
+                               outl(instance->single_value,
+                                    instance->single_reg);
+                               PDEBUG_REG
+                                   ("single_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                    instance->reg_base,
+                                    instance->single_reg - instance->reg_base,
+                                    instance->single_value);
+
+                               // Set correct value for single_read();
+                               instance->single_value_in_fifo =
+                                   instance->single_value;
+
+                               instance->status = ao_status_single_end;
+
+                               // Signal the end.
+                               signaling = 1;
+                       }
+               }
+
+               // Wait for stop.
+               reschedule = 1;
+               break;
+
+       case ao_status_stream_end:
+               if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {   // No FIFO
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+       case ao_status_single_end:
+               if (instance->fifo) {   // Extra registers.
+                       if (status & ME6000_AO_STATUS_BIT_FSM) {        // State machine is working but the status is set to end. Force stop.
+
+                               // Wait for stop.
+                               reschedule = 1;
+                       }
+
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched!
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           ME6000_AO_CTRL_BIT_IMMEDIATE_STOP |
+                           ME6000_AO_CTRL_BIT_STOP;
+                       ctrl &=
+                           ~(ME6000_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       //Reset interrupt latch
+                       inl(instance->irq_reset_reg);
+               } else {        // No extra registers.
+/*
+                               if (status & single_mask)
+                               {// State machine is working but the status is set to end. Force stop.
+
+                                       // Wait for stop.
+                                       reschedule = 1;
+                               }
+*/
+               }
+               break;
+
+               // Stream modes
+       case ao_status_stream_run_wait:
+               if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {   // No FIFO
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+
+               if (status & ME6000_AO_STATUS_BIT_FSM) {        // State machine is working. Waiting for start finish.
+                       instance->status = ao_status_stream_run;
+
+                       // Signal end of this step
+                       signaling = 1;
+               } else {        // State machine is not working.
+                       if (!(status & ME6000_AO_STATUS_BIT_EF)) {      // FIFO is empty. Procedure has started and finish already!
+                               instance->status = ao_status_stream_end;
+
+                               // Signal the end.
+                               signaling = 1;
+                               // Wait for stop.
+                               reschedule = 1;
+                               break;
+                       }
+               }
+
+               // Check timeout.
+               if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {       // Timeout
+                       PDEBUG("Timeout reached.\n");
+                       // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched!
+                       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+                       ctrl = inl(instance->ctrl_reg);
+                       ctrl |=
+                           ME6000_AO_CTRL_BIT_STOP |
+                           ME6000_AO_CTRL_BIT_IMMEDIATE_STOP;
+                       ctrl &=
+                           ~(ME6000_AO_CTRL_BIT_ENABLE_IRQ |
+                             ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG);
+                       outl(ctrl, instance->ctrl_reg);
+                       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->ctrl_reg - instance->reg_base,
+                                  ctrl);
+                       spin_unlock_irqrestore(&instance->subdevice_lock,
+                                              cpu_flags);
+
+                       //Reset interrupt latch
+                       inl(instance->irq_reset_reg);
+
+                       spin_lock(instance->preload_reg_lock);
+                       //Remove from synchronous start. Block triggering from this output.
+                       synch = inl(instance->preload_reg);
+                       synch &=
+                           ~((ME6000_AO_SYNC_HOLD | ME6000_AO_SYNC_EXT_TRIG) <<
+                             instance->ao_idx);
+                       outl(synch, instance->preload_reg);
+                       PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->preload_reg - instance->reg_base,
+                                  synch);
+                       spin_unlock(instance->preload_reg_lock);
+
+                       instance->status = ao_status_stream_end;
+
+                       // Signal the end.
+                       signaling = 1;
+               }
+               // Wait for stop.
+               reschedule = 1;
+               break;
+
+       case ao_status_stream_run:
+               if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {   // No FIFO
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+
+               if (!(status & ME6000_AO_STATUS_BIT_FSM)) {     // State machine is not working. This is an error.
+                       // BROKEN PIPE!
+                       if (!(status & ME6000_AO_STATUS_BIT_EF)) {      // FIFO is empty.
+                               if (me_circ_buf_values(&instance->circ_buf)) {  // Software buffer is not empty.
+                                       if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) { //Finishing work. Requed data shown.
+                                               PDEBUG
+                                                   ("ISM stoped. No data in FIFO. Buffer is not empty.\n");
+                                               instance->status =
+                                                   ao_status_stream_end;
+                                       } else {
+                                               PERROR
+                                                   ("Output stream has been broken. ISM stoped. No data in FIFO. Buffer is not empty.\n");
+                                               instance->status =
+                                                   ao_status_stream_buffer_error;
+                                       }
+                               } else {        // Software buffer is empty.
+                                       PDEBUG
+                                           ("ISM stoped. No data in FIFO. Buffer is empty.\n");
+                                       instance->status = ao_status_stream_end;
+                               }
+                       } else {        // There are still datas in FIFO.
+                               if (me_circ_buf_values(&instance->circ_buf)) {  // Software buffer is not empty.
+                                       PERROR
+                                           ("Output stream has been broken. ISM stoped but some data in FIFO and buffer.\n");
+                               } else {        // Software buffer is empty.
+                                       PERROR
+                                           ("Output stream has been broken. ISM stoped but some data in FIFO. Buffer is empty.\n");
+                               }
+                               instance->status = ao_status_stream_fifo_error;
+
+                       }
+
+                       // Signal the failure.
+                       signaling = 1;
+                       break;
+               }
+               // Wait for stop.
+               reschedule = 1;
+               break;
+
+       case ao_status_stream_end_wait:
+               if (!(instance->fifo & ME6000_AO_HAS_FIFO)) {   // No FIFO
+                       PERROR_CRITICAL
+                           ("Streaming on single device! This feature is not implemented in this version!\n");
+                       instance->status = ao_status_stream_error;
+                       // Signal the end.
+                       signaling = 1;
+                       break;
+               }
+
+               if (!(status & ME6000_AO_STATUS_BIT_FSM)) {     // State machine is not working. Waiting for stop finish.
+                       instance->status = ao_status_stream_end;
+                       signaling = 1;
+               }
+               // State machine is working.
+               reschedule = 1;
+               break;
+
+       default:
+               PERROR_CRITICAL("Status is in wrong state (%d)!\n",
+                               instance->status);
+               instance->status = ao_status_stream_error;
+               // Signal the end.
+               signaling = 1;
+               break;
+
+       }
+
+       if (signaling) {        //Signal it.
+               wake_up_interruptible_all(&instance->wait_queue);
+       }
+
+       if (instance->ao_control_task_flag && reschedule) {     // Reschedule task
+               queue_delayed_work(instance->me6000_workqueue,
+                                  &instance->ao_control_task, 1);
+       } else {
+               PINFO("<%s> Ending control task.\n", __FUNCTION__);
+       }
+
+}
+
+static int me6000_ao_query_range_by_min_max(me_subdevice_t * subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range)
+{
+       me6000_ao_subdevice_t *instance;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if ((*max - *min) < 0) {
+               PERROR("Invalid minimum and maximum values specified.\n");
+               return ME_ERRNO_INVALID_MIN_MAX;
+       }
+
+       if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
+               if ((*max <= (instance->max + 1000)) && (*min >= instance->min)) {
+                       *min = instance->min;
+                       *max = instance->max;
+                       *maxdata = ME6000_AO_MAX_DATA;
+                       *range = 0;
+               } else {
+                       PERROR("No matching range available.\n");
+                       return ME_ERRNO_NO_RANGE;
+               }
+       } else {
+               PERROR("Invalid physical unit specified.\n");
+               return ME_ERRNO_INVALID_UNIT;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_number_ranges(me_subdevice_t * subdevice,
+                                        int unit, int *count)
+{
+       me6000_ao_subdevice_t *instance;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
+               *count = 1;
+       } else {
+               *count = 0;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_range_info(me_subdevice_t * subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata)
+{
+       me6000_ao_subdevice_t *instance;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (range == 0) {
+               *unit = ME_UNIT_VOLT;
+               *min = instance->min;
+               *max = instance->max;
+               *maxdata = ME6000_AO_MAX_DATA;
+       } else {
+               PERROR("Invalid range number specified.\n");
+               return ME_ERRNO_INVALID_RANGE;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_timer(me_subdevice_t * subdevice,
+                                int timer,
+                                int *base_frequency,
+                                long long *min_ticks, long long *max_ticks)
+{
+       me6000_ao_subdevice_t *instance;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (instance->fifo) {   //Streaming device.
+               *base_frequency = ME6000_AO_BASE_FREQUENCY;
+               if (timer == ME_TIMER_ACQ_START) {
+                       *min_ticks = ME6000_AO_MIN_ACQ_TICKS;
+                       *max_ticks = ME6000_AO_MAX_ACQ_TICKS;
+               } else if (timer == ME_TIMER_CONV_START) {
+                       *min_ticks = ME6000_AO_MIN_CHAN_TICKS;
+                       *max_ticks = ME6000_AO_MAX_CHAN_TICKS;
+               }
+       } else {                //Not streaming device!
+               *base_frequency = 0;
+               *min_ticks = 0;
+               *max_ticks = 0;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       me6000_ao_subdevice_t *instance;
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *number = 1;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       me6000_ao_subdevice_t *instance;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *type = ME_TYPE_AO;
+       *subtype =
+           (instance->
+            fifo & ME6000_AO_HAS_FIFO) ? ME_SUBTYPE_STREAMING :
+           ME_SUBTYPE_SINGLE;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       me6000_ao_subdevice_t *instance;
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       *caps =
+           ME_CAPS_AO_TRIG_SYNCHRONOUS | ((instance->fifo) ? ME_CAPS_AO_FIFO :
+                                          ME_CAPS_NONE);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_ao_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                              int cap, int *args, int count)
+{
+       me6000_ao_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       instance = (me6000_ao_subdevice_t *) subdevice;
+
+       PDEBUG("executed. idx=%d\n", instance->ao_idx);
+
+       if (count != 1) {
+               PERROR("Invalid capability argument count.\n");
+               return ME_ERRNO_INVALID_CAP_ARG_COUNT;
+       }
+
+       switch (cap) {
+       case ME_CAP_AI_FIFO_SIZE:
+               args[0] = (instance->fifo) ? ME6000_AO_FIFO_COUNT : 0;
+               break;
+
+       case ME_CAP_AI_BUFFER_SIZE:
+               args[0] =
+                   (instance->circ_buf.buf) ? ME6000_AO_CIRC_BUF_COUNT : 0;
+               break;
+
+       default:
+               PERROR("Invalid capability.\n");
+               err = ME_ERRNO_INVALID_CAP;
+               args[0] = 0;
+       }
+
+       return err;
+}
diff --git a/drivers/staging/meilhaus/me6000_ao.h b/drivers/staging/meilhaus/me6000_ao.h
new file mode 100644 (file)
index 0000000..9629649
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * @file me6000_ao.h
+ *
+ * @brief Meilhaus ME-6000 analog output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME6000_AO_H_
+#define _ME6000_AO_H_
+
+#include <linux/version.h>
+#include "mesubdevice.h"
+#include "mecirc_buf.h"
+#include "meioctl.h"
+
+#ifdef __KERNEL__
+
+#define ME6000_AO_MAX_SUBDEVICES       16
+#define ME6000_AO_FIFO_COUNT           8192
+
+#define ME6000_AO_BASE_FREQUENCY       33000000L
+
+#define ME6000_AO_MIN_ACQ_TICKS                0LL
+#define ME6000_AO_MAX_ACQ_TICKS                0LL
+
+#define ME6000_AO_MIN_CHAN_TICKS       66LL
+#define ME6000_AO_MAX_CHAN_TICKS       0xFFFFFFFFLL
+
+#define ME6000_AO_MIN_RANGE                    -10000000
+#define ME6000_AO_MAX_RANGE                    9999694
+
+#define ME6000_AO_MIN_RANGE_HIGH       0
+#define ME6000_AO_MAX_RANGE_HIGH       49999237
+
+#define ME6000_AO_MAX_DATA                     0xFFFF
+
+#ifdef ME_SYNAPSE
+# define ME6000_AO_CIRC_BUF_SIZE_ORDER                 8       // 2^n PAGES =>> Maximum value of 1MB for Synapse
+#else
+# define ME6000_AO_CIRC_BUF_SIZE_ORDER                 5       // 2^n PAGES =>> 128KB
+#endif
+#define ME6000_AO_CIRC_BUF_SIZE                PAGE_SIZE<<ME6000_AO_CIRC_BUF_SIZE_ORDER        // Buffer size in bytes.
+
+#  ifdef _CBUFF_32b_t
+#   define ME6000_AO_CIRC_BUF_COUNT    ((ME6000_AO_CIRC_BUF_SIZE) / sizeof(uint32_t))  // Size in values
+#  else
+#   define ME6000_AO_CIRC_BUF_COUNT    ((ME6000_AO_CIRC_BUF_SIZE) / sizeof(uint16_t))  // Size in values
+#  endif
+
+#  define ME6000_AO_CONTINOUS                                  0x0
+#  define ME6000_AO_WRAP_MODE                                  0x1
+#  define ME6000_AO_HW_MODE                                            0x2
+
+#  define ME6000_AO_HW_WRAP_MODE                               (ME6000_AO_WRAP_MODE | ME6000_AO_HW_MODE)
+#  define ME6000_AO_SW_WRAP_MODE                               ME6000_AO_WRAP_MODE
+
+#  define ME6000_AO_INF_STOP_MODE                              0x0
+#  define ME6000_AO_ACQ_STOP_MODE                              0x1
+#  define ME6000_AO_SCAN_STOP_MODE                             0x2
+
+#  define ME6000_AO_EXTRA_HARDWARE                             0x1
+#  define ME6000_AO_HAS_FIFO                                   0x2
+
+typedef enum ME6000_AO_STATUS {
+       ao_status_none = 0,
+       ao_status_single_configured,
+       ao_status_single_run_wait,
+       ao_status_single_run,
+       ao_status_single_end_wait,
+       ao_status_single_end,
+       ao_status_stream_configured,
+       ao_status_stream_run_wait,
+       ao_status_stream_run,
+       ao_status_stream_end_wait,
+       ao_status_stream_end,
+       ao_status_stream_fifo_error,
+       ao_status_stream_buffer_error,
+       ao_status_stream_error,
+       ao_status_last
+} ME6000_AO_STATUS;
+
+typedef struct me6000_ao_timeout {
+       unsigned long start_time;
+       unsigned long delay;
+} me6000_ao_timeout_t;
+
+/**
+ * @brief The ME-6000 analog output subdevice class.
+ */
+typedef struct me6000_ao_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                                    /**< The subdevice base class. */
+       unsigned int ao_idx;
+
+       /* Attributes */
+       spinlock_t subdevice_lock;                              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *preload_reg_lock;                   /**< Spin lock to protect preload_reg from concurrent access. */
+
+       uint32_t *preload_flags;
+       uint32_t *triggering_flags;
+
+       /* Hardware feautres */
+       unsigned int irq;                                               /**< The interrupt request number assigned by the PCI BIOS. */
+       int fifo;                                                               /**< If set this device has a FIFO. */
+
+       //Range
+       int min;
+       int max;
+
+       int single_value;                                               /**< Mirror of the output value in single mode. */
+       int single_value_in_fifo;                               /**< Mirror of the value written in single mode. */
+       uint32_t ctrl_trg;                                              /**< Mirror of the trigger settings. */
+
+       volatile int mode;                                              /**< Flags used for storing SW wraparound setup*/
+       int stop_mode;                                                  /**< The user defined stop condition flag. */
+       unsigned int start_mode;
+       unsigned int stop_count;                                /**< The user defined dates presentation end count. */
+       unsigned int stop_data_count;                   /**< The stop presentation count. */
+       unsigned int data_count;                                /**< The real presentation count. */
+       unsigned int preloaded_count;                   /**< The next data addres in buffer. <= for wraparound mode. */
+       int hardware_stop_delay;                                /**< The time that stop can take. This is only to not show hardware bug to user. */
+
+       volatile enum ME6000_AO_STATUS status;  /**< The current stream status flag. */
+       me6000_ao_timeout_t timeout;                    /**< The timeout for start in blocking and non-blocking mode. */
+
+                                                                       /* Registers *//**< All registers are 32 bits long. */
+       unsigned long ctrl_reg;
+       unsigned long status_reg;
+       unsigned long fifo_reg;
+       unsigned long single_reg;
+       unsigned long timer_reg;
+       unsigned long irq_status_reg;
+       unsigned long preload_reg;
+       unsigned long irq_reset_reg;
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+
+       /* Software buffer */
+       me_circ_buf_t circ_buf;                                 /**< Circular buffer holding measurment data. */
+       wait_queue_head_t wait_queue;                   /**< Wait queue to put on tasks waiting for data to arrive. */
+
+       struct workqueue_struct *me6000_workqueue;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+       struct work_struct ao_control_task;
+#else
+       struct delayed_work ao_control_task;
+#endif
+
+       volatile int ao_control_task_flag;              /**< Flag controling reexecuting of control task */
+
+} me6000_ao_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-6000 analog output subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
+ * @param preload_flags Pointer to spin lock protecting the hold&trigger register from concurrent access.
+ * @param ao_idx Subdevice number.
+ * @param fifo Flag set if subdevice has hardware FIFO.
+ * @param irq IRQ number.
+ * @param high_range Flag set if subdevice has high curren output.
+ * @param me6000_wq Queue for asynchronous task (1 queue for all subdevice on 1 board).
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me6000_ao_subdevice_t *me6000_ao_constructor(uint32_t reg_base,
+                                            spinlock_t * preload_reg_lock,
+                                            uint32_t * preload_flags,
+                                            uint32_t * triggering_flags,
+                                            int ao_idx,
+                                            int fifo,
+                                            int irq,
+                                            int high_range,
+                                            struct workqueue_struct
+                                            *me6000_wq);
+
+#endif //__KERNEL__
+#endif //_ME6000_AO_H_
diff --git a/drivers/staging/meilhaus/me6000_ao_reg.h b/drivers/staging/meilhaus/me6000_ao_reg.h
new file mode 100644 (file)
index 0000000..eb8f46e
--- /dev/null
@@ -0,0 +1,177 @@
+/**
+ * @file me6000_ao_reg.h
+ *
+ * @brief ME-6000 analog output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME6000_AO_REG_H_
+#define _ME6000_AO_REG_H_
+
+#ifdef __KERNEL__
+
+// AO
+#define ME6000_AO_00_CTRL_REG                                  0x00    // R/W
+#define ME6000_AO_00_STATUS_REG                                        0x04    // R/_
+#define ME6000_AO_00_FIFO_REG                                  0x08    // _/W
+#define ME6000_AO_00_SINGLE_REG                                        0x0C    // R/W
+#define ME6000_AO_00_TIMER_REG                                 0x10    // _/W
+
+#define ME6000_AO_01_CTRL_REG                                  0x18    // R/W
+#define ME6000_AO_01_STATUS_REG                                        0x1C    // R/_
+#define ME6000_AO_01_FIFO_REG                                  0x20    // _/W
+#define ME6000_AO_01_SINGLE_REG                                        0x24    // R/W
+#define ME6000_AO_01_TIMER_REG                                 0x28    // _/W
+
+#define ME6000_AO_02_CTRL_REG                                  0x30    // R/W
+#define ME6000_AO_02_STATUS_REG                                        0x34    // R/_
+#define ME6000_AO_02_FIFO_REG                                  0x38    // _/W
+#define ME6000_AO_02_SINGLE_REG                                        0x3C    // R/W
+#define ME6000_AO_02_TIMER_REG                                 0x40    // _/W
+
+#define ME6000_AO_03_CTRL_REG                                  0x48    // R/W
+#define ME6000_AO_03_STATUS_REG                                        0x4C    // R/_
+#define ME6000_AO_03_FIFO_REG                                  0x50    // _/W
+#define ME6000_AO_03_SINGLE_REG                                        0x54    // R/W
+#define ME6000_AO_03_TIMER_REG                                 0x58    // _/W
+
+#define ME6000_AO_SINGLE_STATUS_REG                            0xA4    // R/_
+#define ME6000_AO_SINGLE_STATUS_OFFSET                 4       //The first single subdevice => bit 0 in ME6000_AO_SINGLE_STATUS_REG.
+
+#define ME6000_AO_04_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_04_SINGLE_REG                                        0x74    // _/W
+
+#define ME6000_AO_05_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_05_SINGLE_REG                                        0x78    // _/W
+
+#define ME6000_AO_06_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_06_SINGLE_REG                                        0x7C    // _/W
+
+#define ME6000_AO_07_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_07_SINGLE_REG                                        0x80    // _/W
+
+#define ME6000_AO_08_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_08_SINGLE_REG                                        0x84    // _/W
+
+#define ME6000_AO_09_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_09_SINGLE_REG                                        0x88    // _/W
+
+#define ME6000_AO_10_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_10_SINGLE_REG                                        0x8C    // _/W
+
+#define ME6000_AO_11_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_11_SINGLE_REG                                        0x90    // _/W
+
+#define ME6000_AO_12_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_12_SINGLE_REG                                        0x94    // _/W
+
+#define ME6000_AO_13_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_13_SINGLE_REG                                        0x98    // _/W
+
+#define ME6000_AO_14_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_14_SINGLE_REG                                        0x9C    // _/W
+
+#define ME6000_AO_15_STATUS_REG                                        ME6000_AO_SINGLE_STATUS_REG
+#define ME6000_AO_15_SINGLE_REG                                        0xA0    // _/W
+
+//ME6000_AO_CTRL_REG
+#define ME6000_AO_MODE_SINGLE                                  0x00
+#define ME6000_AO_MODE_WRAPAROUND                              0x01
+#define ME6000_AO_MODE_CONTINUOUS                              0x02
+#define ME6000_AO_CTRL_MODE_MASK                               (ME6000_AO_MODE_WRAPAROUND | ME6000_AO_MODE_CONTINUOUS)
+
+#define ME6000_AO_CTRL_BIT_MODE_WRAPAROUND             0x001
+#define ME6000_AO_CTRL_BIT_MODE_CONTINUOUS             0x002
+#define ME6000_AO_CTRL_BIT_STOP                                        0x004
+#define ME6000_AO_CTRL_BIT_ENABLE_FIFO                 0x008
+#define ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG              0x010
+#define ME6000_AO_CTRL_BIT_EX_TRIG_EDGE                        0x020
+#define ME6000_AO_CTRL_BIT_ENABLE_IRQ                  0x040
+#define ME6000_AO_CTRL_BIT_IMMEDIATE_STOP              0x080
+#define ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH   0x800
+
+//ME6000_AO_STATUS_REG
+#define ME6000_AO_STATUS_BIT_FSM                               0x01
+#define ME6000_AO_STATUS_BIT_FF                                        0x02
+#define ME6000_AO_STATUS_BIT_HF                                        0x04
+#define ME6000_AO_STATUS_BIT_EF                                        0x08
+
+#define ME6000_AO_PRELOAD_REG                                  0xA8    // R/W    ///ME6000_AO_SYNC_REG <==> ME6000_AO_PRELOAD_REG
+/*
+#define ME6000_AO_SYNC_HOLD_0                                  0x00000001
+#define ME6000_AO_SYNC_HOLD_1                                  0x00000002
+#define ME6000_AO_SYNC_HOLD_2                                  0x00000004
+#define ME6000_AO_SYNC_HOLD_3                                  0x00000008
+#define ME6000_AO_SYNC_HOLD_4                                  0x00000010
+#define ME6000_AO_SYNC_HOLD_5                                  0x00000020
+#define ME6000_AO_SYNC_HOLD_6                                  0x00000040
+#define ME6000_AO_SYNC_HOLD_7                                  0x00000080
+#define ME6000_AO_SYNC_HOLD_8                                  0x00000100
+#define ME6000_AO_SYNC_HOLD_9                                  0x00000200
+#define ME6000_AO_SYNC_HOLD_10                                 0x00000400
+#define ME6000_AO_SYNC_HOLD_11                                 0x00000800
+#define ME6000_AO_SYNC_HOLD_12                                 0x00001000
+#define ME6000_AO_SYNC_HOLD_13                                 0x00002000
+#define ME6000_AO_SYNC_HOLD_14                                 0x00004000
+#define ME6000_AO_SYNC_HOLD_15                                 0x00008000
+*/
+#define ME6000_AO_SYNC_HOLD                                            0x00000001
+/*
+#define ME6000_AO_SYNC_EXT_TRIG_0                              0x00010000
+#define ME6000_AO_SYNC_EXT_TRIG_1                              0x00020000
+#define ME6000_AO_SYNC_EXT_TRIG_2                              0x00040000
+#define ME6000_AO_SYNC_EXT_TRIG_3                              0x00080000
+#define ME6000_AO_SYNC_EXT_TRIG_4                              0x00100000
+#define ME6000_AO_SYNC_EXT_TRIG_5                              0x00200000
+#define ME6000_AO_SYNC_EXT_TRIG_6                              0x00400000
+#define ME6000_AO_SYNC_EXT_TRIG_7                              0x00800000
+#define ME6000_AO_SYNC_EXT_TRIG_8                              0x01000000
+#define ME6000_AO_SYNC_EXT_TRIG_9                              0x02000000
+#define ME6000_AO_SYNC_EXT_TRIG_10                             0x04000000
+#define ME6000_AO_SYNC_EXT_TRIG_11                             0x08000000
+#define ME6000_AO_SYNC_EXT_TRIG_12                             0x10000000
+#define ME6000_AO_SYNC_EXT_TRIG_13                             0x20000000
+#define ME6000_AO_SYNC_EXT_TRIG_14                             0x40000000
+#define ME6000_AO_SYNC_EXT_TRIG_15                             0x80000000
+*/
+#define ME6000_AO_SYNC_EXT_TRIG                                        0x00010000
+
+#define ME6000_AO_EXT_TRIG                                             0x80000000
+
+// AO-IRQ
+#define ME6000_AO_IRQ_STATUS_REG                               0x60    // R/_
+#define ME6000_AO_00_IRQ_RESET_REG                             0x64    // R/_
+#define ME6000_AO_01_IRQ_RESET_REG                             0x68    // R/_
+#define ME6000_AO_02_IRQ_RESET_REG                             0x6C    // R/_
+#define ME6000_AO_03_IRQ_RESET_REG                             0x70    // R/_
+
+#define ME6000_IRQ_STATUS_BIT_0                                        0x01
+#define ME6000_IRQ_STATUS_BIT_1                                        0x02
+#define ME6000_IRQ_STATUS_BIT_2                                        0x04
+#define ME6000_IRQ_STATUS_BIT_3                                        0x08
+
+#define ME6000_IRQ_STATUS_BIT_AO_HF                            ME6000_IRQ_STATUS_BIT_0
+
+//DUMY register
+#define ME6000_AO_DUMY                                                                 0xFC
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me6000_device.c b/drivers/staging/meilhaus/me6000_device.c
new file mode 100644 (file)
index 0000000..fee4c58
--- /dev/null
@@ -0,0 +1,211 @@
+/**
+ * @file me6000_device.c
+ *
+ * @brief Device class template implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "mefirmware.h"
+
+#include "mesubdevice.h"
+#include "medebug.h"
+#include "medevice.h"
+#include "me6000_reg.h"
+#include "me6000_device.h"
+#include "meplx_reg.h"
+#include "me6000_dio.h"
+#include "me6000_ao.h"
+
+/**
+ * @brief Global variable.
+ * This is working queue for runing a separate atask that will be responsible for work status (start, stop, timeouts).
+ */
+static struct workqueue_struct *me6000_workqueue;
+
+me_device_t *me6000_pci_constructor(struct pci_dev *pci_device)
+{
+       me6000_device_t *me6000_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+       int high_range = 0;
+       int fifo;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me6000_device = kmalloc(sizeof(me6000_device_t), GFP_KERNEL);
+
+       if (!me6000_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(me6000_device, 0, sizeof(me6000_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me6000_device, pci_device);
+
+       if (err) {
+               kfree(me6000_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Download the xilinx firmware */
+       err = me_xilinx_download(me6000_device->base.info.pci.reg_bases[1],
+                                me6000_device->base.info.pci.reg_bases[2],
+                                &pci_device->dev, "me6000.bin");
+
+       if (err) {
+               me_device_deinit((me_device_t *) me6000_device);
+               kfree(me6000_device);
+               PERROR("Can't download firmware.\n");
+               return NULL;
+       }
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           me6000_versions_get_device_index(me6000_device->base.info.pci.
+                                            device_id);
+
+       // Initialize spin lock .
+       spin_lock_init(&me6000_device->preload_reg_lock);
+       spin_lock_init(&me6000_device->dio_ctrl_reg_lock);
+
+       /* Create digital input/output instances. */
+       for (i = 0; i < me6000_versions[version_idx].dio_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me6000_dio_constructor(me6000_device->
+                                                             base.info.pci.
+                                                             reg_bases[3], i,
+                                                             &me6000_device->
+                                                             dio_ctrl_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me6000_device);
+                       kfree(me6000_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me6000_device->base.slist,
+                                           subdevice);
+       }
+
+       /* Create analog output instances. */
+       for (i = 0; i < me6000_versions[version_idx].ao_subdevices; i++) {
+               high_range = ((i == 8)
+                             &&
+                             ((me6000_device->base.info.pci.device_id ==
+                               PCI_DEVICE_ID_MEILHAUS_ME6359)
+                              || (me6000_device->base.info.pci.device_id ==
+                                  PCI_DEVICE_ID_MEILHAUS_ME6259)
+                             )
+                   )? 1 : 0;
+
+               fifo =
+                   (i <
+                    me6000_versions[version_idx].
+                    ao_fifo) ? ME6000_AO_HAS_FIFO : 0x0;
+               fifo |= (i < 4) ? ME6000_AO_EXTRA_HARDWARE : 0x0;
+
+               subdevice =
+                   (me_subdevice_t *) me6000_ao_constructor(me6000_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            &me6000_device->
+                                                            preload_reg_lock,
+                                                            &me6000_device->
+                                                            preload_flags,
+                                                            &me6000_device->
+                                                            triggering_flags,
+                                                            i, fifo,
+                                                            me6000_device->
+                                                            base.irq,
+                                                            high_range,
+                                                            me6000_workqueue);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me6000_device);
+                       kfree(me6000_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me6000_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me6000_device;
+}
+
+// Init and exit of module.
+
+static int __init me6000_init(void)
+{
+       PDEBUG("executed.\n");
+
+       me6000_workqueue = create_singlethread_workqueue("me6000");
+       return 0;
+}
+
+static void __exit me6000_exit(void)
+{
+       PDEBUG("executed.\n");
+
+       flush_workqueue(me6000_workqueue);
+       destroy_workqueue(me6000_workqueue);
+}
+
+module_init(me6000_init);
+module_exit(me6000_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for ME-6000 Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-6000 Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me6000_pci_constructor);
diff --git a/drivers/staging/meilhaus/me6000_device.h b/drivers/staging/meilhaus/me6000_device.h
new file mode 100644 (file)
index 0000000..18cc7d1
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * @file me6000_device.h
+ *
+ * @brief ME-6000 device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME6000_DEVICE_H
+#define _ME6000_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding ME-6000 device capabilities.
+ */
+typedef struct me6000_version {
+       uint16_t device_id;
+       unsigned int dio_subdevices;
+       unsigned int ao_subdevices;
+       unsigned int ao_fifo;   //How many devices have FIFO
+} me6000_version_t;
+
+/**
+ * @brief ME-6000 device capabilities.
+ */
+static me6000_version_t me6000_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME6004, 0, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME6008, 0, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME600F, 0, 16, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6014, 0, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME6018, 0, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME601F, 0, 16, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6034, 0, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME6038, 0, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME603F, 0, 16, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6104, 0, 4, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME6108, 0, 8, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME610F, 0, 16, 4},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6114, 0, 4, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME6118, 0, 8, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME611F, 0, 16, 4},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6134, 0, 4, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME6138, 0, 8, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME613F, 0, 16, 4},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6044, 2, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME6048, 2, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME604F, 2, 16, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6054, 2, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME6058, 2, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME605F, 2, 16, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6074, 2, 4, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME6078, 2, 8, 0},
+       {PCI_DEVICE_ID_MEILHAUS_ME607F, 2, 16, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6144, 2, 4, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME6148, 2, 8, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME614F, 2, 16, 4},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6154, 2, 4, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME6158, 2, 8, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME615F, 2, 16, 4},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6174, 2, 4, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME6178, 2, 8, 4},
+       {PCI_DEVICE_ID_MEILHAUS_ME617F, 2, 16, 4},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6259, 2, 9, 0},
+
+       {PCI_DEVICE_ID_MEILHAUS_ME6359, 2, 9, 4},
+
+       {0},
+};
+
+#define ME6000_DEVICE_VERSIONS (sizeof(me6000_versions) / sizeof(me6000_version_t) - 1)        /**< Returns the number of entries in #me6000_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me6000_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me6000_versions.
+ */
+static inline unsigned int me6000_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME6000_DEVICE_VERSIONS; i++)
+               if (me6000_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-6000 device class structure.
+ */
+typedef struct me6000_device {
+       me_device_t base;                       /**< The Meilhaus device base class. */
+
+       /* Child class attributes. */
+       spinlock_t preload_reg_lock;            /**< Guards the preload register. */
+       uint32_t preload_flags;
+       uint32_t triggering_flags;
+
+       spinlock_t dio_ctrl_reg_lock;
+} me6000_device_t;
+
+/**
+ * @brief The ME-6000 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-6000 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me6000_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me6000_dio.c b/drivers/staging/meilhaus/me6000_dio.c
new file mode 100644 (file)
index 0000000..07f1069
--- /dev/null
@@ -0,0 +1,415 @@
+/**
+ * @file me6000_dio.c
+ *
+ * @brief ME-6000 digital input/output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me6000_dio_reg.h"
+#include "me6000_dio.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me6000_dio_io_reset_subdevice(struct me_subdevice *subdevice,
+                                        struct file *filep, int flags)
+{
+       me6000_dio_subdevice_t *instance;
+       uint8_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me6000_dio_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inb(instance->ctrl_reg);
+       mode &= ~(0x3 << (instance->dio_idx * 2));
+       outb(mode, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, mode);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outb(0x00, instance->port_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, 0x00);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_dio_io_single_config(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int single_config,
+                                      int ref,
+                                      int trig_chan,
+                                      int trig_type, int trig_edge, int flags)
+{
+       me6000_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+       int size =
+           flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
+                    | ME_IO_SINGLE_CONFIG_DIO_WORD |
+                    ME_IO_SINGLE_CONFIG_DIO_DWORD);
+
+       PDEBUG("executed.\n");
+
+       instance = (me6000_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inb(instance->ctrl_reg);
+       switch (size) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                               mode &=
+                                   ~((ME6000_DIO_CTRL_BIT_MODE_0 |
+                                      ME6000_DIO_CTRL_BIT_MODE_1) <<
+                                     (instance->dio_idx * 2));
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               mode &=
+                                   ~((ME6000_DIO_CTRL_BIT_MODE_0 |
+                                      ME6000_DIO_CTRL_BIT_MODE_1) <<
+                                     (instance->dio_idx * 2));
+                               mode |=
+                                   ME6000_DIO_CTRL_BIT_MODE_0 << (instance->
+                                                                  dio_idx * 2);
+                       } else {
+                               PERROR
+                                   ("Invalid port configuration specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outb(mode, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, mode);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_dio_io_single_read(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int *value, int time_out, int flags)
+{
+       me6000_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me6000_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
+                                             ME6000_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+                       if ((mode ==
+                            (ME6000_DIO_CTRL_BIT_MODE_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value =
+                                   inb(instance->port_reg) & (0x1 << channel);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
+                                             ME6000_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+                       if ((mode ==
+                            (ME6000_DIO_CTRL_BIT_MODE_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value = inb(instance->port_reg) & 0x00FF;
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_dio_io_single_write(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int value, int time_out, int flags)
+{
+       me6000_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+       uint8_t byte;
+
+       PDEBUG("executed.\n");
+
+       instance = (me6000_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
+                                             ME6000_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME6000_DIO_CTRL_BIT_MODE_0 <<
+                            (instance->dio_idx * 2))) {
+                               byte = inb(instance->port_reg) & 0x00FF;
+
+                               if (value)
+                                       byte |= 0x1 << channel;
+                               else
+                                       byte &= ~(0x1 << channel);
+
+                               outb(byte, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
+                                             ME6000_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME6000_DIO_CTRL_BIT_MODE_0 <<
+                            (instance->dio_idx * 2))) {
+                               outb(value, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me6000_dio_query_number_channels(me_subdevice_t * subdevice,
+                                           int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_dio_query_subdevice_type(me_subdevice_t * subdevice,
+                                          int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DIO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me6000_dio_query_subdevice_caps(me_subdevice_t * subdevice,
+                                          int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_DIR_BYTE;
+       return ME_ERRNO_SUCCESS;
+}
+
+me6000_dio_subdevice_t *me6000_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock)
+{
+       me6000_dio_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me6000_dio_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me6000_dio_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+
+       /* Set the subdevice ports */
+       subdevice->ctrl_reg = reg_base + ME6000_DIO_CTRL_REG;
+       switch (dio_idx) {
+       case 0:
+               subdevice->port_reg = reg_base + ME6000_DIO_PORT_0_REG;
+               break;
+       case 1:
+               subdevice->port_reg = reg_base + ME6000_DIO_PORT_1_REG;
+               break;
+       default:
+               err = ME_ERRNO_INVALID_SUBDEVICE;
+       }
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save digital i/o index */
+       subdevice->dio_idx = dio_idx;
+
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me6000_dio_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me6000_dio_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me6000_dio_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me6000_dio_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me6000_dio_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me6000_dio_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me6000_dio_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me6000_dio.h b/drivers/staging/meilhaus/me6000_dio.h
new file mode 100644 (file)
index 0000000..858bec1
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * @file me6000_dio.h
+ *
+ * @brief ME-6000 digital input/output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME6000_DIO_H_
+#define _ME6000_DIO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me6000_dio_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+       unsigned int dio_idx;                   /**< The index of the digital i/o on the device. */
+
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Register to configure the port direction. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me6000_dio_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-6000 digital input/ouput subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param dio_idx The index of the digital i/o port on the device.
+ * @param ctrl_reg_lock Spin lock protecting the control register.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me6000_dio_subdevice_t *me6000_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me6000_dio_reg.h b/drivers/staging/meilhaus/me6000_dio_reg.h
new file mode 100644 (file)
index 0000000..e67a791
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * @file me6000_dio_reg.h
+ *
+ * @brief ME-6000 digital input/output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME6000_DIO_REG_H_
+#define _ME6000_DIO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME6000_DIO_CTRL_REG                            0x00    // R/W
+#define ME6000_DIO_PORT_0_REG                  0x01    // R/W
+#define ME6000_DIO_PORT_1_REG                  0x02    // R/W
+#define ME6000_DIO_PORT_REG                            ME6000_DIO_PORT_0_REG   // R/W
+
+#define ME6000_DIO_CTRL_BIT_MODE_0             0x01
+#define ME6000_DIO_CTRL_BIT_MODE_1             0x02
+#define ME6000_DIO_CTRL_BIT_MODE_2             0x04
+#define ME6000_DIO_CTRL_BIT_MODE_3             0x08
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me6000_reg.h b/drivers/staging/meilhaus/me6000_reg.h
new file mode 100644 (file)
index 0000000..d352730
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * @file me6000_reg.h
+ *
+ * @brief ME-6000 device register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME6000_REG_H_
+#define _ME6000_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME6000_INIT_XILINX_REG         0xAC    // R/-
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8100_device.c b/drivers/staging/meilhaus/me8100_device.c
new file mode 100644 (file)
index 0000000..1fb79e4
--- /dev/null
@@ -0,0 +1,187 @@
+/**
+ * @file me8100_device.c
+ *
+ * @brief ME-8100 device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "me8100_device.h"
+#include "mesubdevice.h"
+#include "me8100_di.h"
+#include "me8100_do.h"
+#include "me8254.h"
+
+me_device_t *me8100_pci_constructor(struct pci_dev *pci_device)
+{
+       me8100_device_t *me8100_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me8100_device = kmalloc(sizeof(me8100_device_t), GFP_KERNEL);
+
+       if (!me8100_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(me8100_device, 0, sizeof(me8100_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me8100_device, pci_device);
+
+       if (err) {
+               kfree(me8100_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           me8100_versions_get_device_index(me8100_device->base.info.pci.
+                                            device_id);
+
+       // Initialize spin lock .
+       spin_lock_init(&me8100_device->dio_ctrl_reg_lock);
+       spin_lock_init(&me8100_device->ctr_ctrl_reg_lock);
+       spin_lock_init(&me8100_device->clk_src_reg_lock);
+
+       // Create subdevice instances.
+
+       for (i = 0; i < me8100_versions[version_idx].di_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8100_di_constructor(me8100_device->
+                                                            base.info.pci.
+                                                            reg_bases[2],
+                                                            me8100_device->
+                                                            base.info.pci.
+                                                            reg_bases[1], i,
+                                                            me8100_device->
+                                                            base.irq,
+                                                            &me8100_device->
+                                                            dio_ctrl_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me8100_device);
+                       kfree(me8100_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me8100_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me8100_versions[version_idx].do_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8100_do_constructor(me8100_device->
+                                                            base.info.pci.
+                                                            reg_bases[2], i,
+                                                            &me8100_device->
+                                                            dio_ctrl_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me8100_device);
+                       kfree(me8100_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me8100_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me8100_versions[version_idx].ctr_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8254_constructor(me8100_device->base.
+                                                         info.pci.device_id,
+                                                         me8100_device->base.
+                                                         info.pci.reg_bases[2],
+                                                         0, i,
+                                                         &me8100_device->
+                                                         ctr_ctrl_reg_lock,
+                                                         &me8100_device->
+                                                         clk_src_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me8100_device);
+                       kfree(me8100_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me8100_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me8100_device;
+}
+
+// Init and exit of module.
+
+static int __init me8100_init(void)
+{
+       PDEBUG("executed.\n.");
+       return ME_ERRNO_SUCCESS;
+}
+
+static void __exit me8100_exit(void)
+{
+       PDEBUG("executed.\n.");
+}
+
+module_init(me8100_init);
+
+module_exit(me8100_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for Template Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus Template Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me8100_pci_constructor);
diff --git a/drivers/staging/meilhaus/me8100_device.h b/drivers/staging/meilhaus/me8100_device.h
new file mode 100644 (file)
index 0000000..44c42ef
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * @file me8100_device.h
+ *
+ * @brief ME-8100 device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8100_DEVICE_H
+#define _ME8100_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding ME-8100 device capabilities.
+ */
+typedef struct me8100_version {
+       uint16_t device_id;
+       unsigned int di_subdevices;
+       unsigned int do_subdevices;
+       unsigned int ctr_subdevices;
+} me8100_version_t;
+
+/**
+ * @brief Device capabilities.
+ */
+static me8100_version_t me8100_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME8100_A, 1, 1, 3},
+       {PCI_DEVICE_ID_MEILHAUS_ME8100_B, 2, 2, 3},
+       {0},
+};
+
+#define ME8100_DEVICE_VERSIONS (sizeof(me8100_versions) / sizeof(me8100_version_t) - 1)        /**< Returns the number of entries in #me8100_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me8100_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me8100_versions.
+ */
+static inline unsigned int me8100_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME8100_DEVICE_VERSIONS; i++)
+               if (me8100_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-8100 device class structure.
+ */
+typedef struct me8100_device {
+       me_device_t base;                       /**< The Meilhaus device base class. */
+
+       /* Child class attributes. */
+       spinlock_t dio_ctrl_reg_lock;
+       spinlock_t ctr_ctrl_reg_lock;
+       spinlock_t clk_src_reg_lock;
+} me8100_device_t;
+
+/**
+ * @brief The ME-8100 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-8100 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me8100_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8100_di.c b/drivers/staging/meilhaus/me8100_di.c
new file mode 100644 (file)
index 0000000..0f14637
--- /dev/null
@@ -0,0 +1,693 @@
+/**
+ * @file me8100_di.c
+ *
+ * @brief ME-8100 digital input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include "medefines.h"
+#include "meerror.h"
+
+#include "meids.h"
+#include "medebug.h"
+#include "meplx_reg.h"
+#include "me8100_reg.h"
+#include "me8100_di_reg.h"
+#include "me8100_di.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me8100_di_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me8100_di_subdevice_t *instance;
+       unsigned short ctrl;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->ctrl_reg_lock);
+       ctrl = inw(instance->ctrl_reg);
+       ctrl &= ~(ME8100_DIO_CTRL_BIT_INTB_1 | ME8100_DIO_CTRL_BIT_INTB_0);
+       outw(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outw(0, instance->mask_reg);
+       PDEBUG_REG("mask_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->mask_reg - instance->reg_base, 0);
+       outw(0, instance->pattern_reg);
+       PDEBUG_REG("pattern_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->pattern_reg - instance->reg_base, 0);
+       instance->rised = -1;
+       instance->irq_count = 0;
+       instance->filtering_flag = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       outl(PLX_INTCSR_LOCAL_INT1_EN |
+            PLX_INTCSR_LOCAL_INT1_POL |
+            PLX_INTCSR_LOCAL_INT2_EN |
+            PLX_INTCSR_LOCAL_INT2_POL |
+            PLX_INTCSR_PCI_INT_EN, instance->irq_status_reg);
+       PDEBUG_REG("plx:irq_status_reg outl(0x%lX)=0x%x\n",
+                  instance->irq_status_reg,
+                  PLX_INTCSR_LOCAL_INT1_EN | PLX_INTCSR_LOCAL_INT1_POL |
+                  PLX_INTCSR_LOCAL_INT2_EN | PLX_INTCSR_LOCAL_INT2_POL |
+                  PLX_INTCSR_PCI_INT_EN);
+
+       wake_up_interruptible_all(&instance->wait_queue);
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_di_io_irq_start(me_subdevice_t * subdevice,
+                                 struct file *filep,
+                                 int channel,
+                                 int irq_source,
+                                 int irq_edge, int irq_arg, int flags)
+{
+       me8100_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint16_t ctrl;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
+               if (flags &
+                   ~(ME_IO_IRQ_START_PATTERN_FILTERING |
+                     ME_IO_IRQ_START_DIO_WORD)) {
+                       PERROR("Invalid flag specified.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+
+               if (irq_edge != ME_IRQ_EDGE_NOT_USED) {
+                       PERROR("Invalid irq edge specified.\n");
+                       return ME_ERRNO_INVALID_IRQ_EDGE;
+               }
+       } else if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               if (flags &
+                   ~(ME_IO_IRQ_START_EXTENDED_STATUS |
+                     ME_IO_IRQ_START_DIO_WORD)) {
+                       PERROR("Invalid flag specified.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+
+               if (irq_edge != ME_IRQ_EDGE_ANY) {
+                       PERROR("Invalid irq edge specified.\n");
+                       return ME_ERRNO_INVALID_IRQ_EDGE;
+               }
+
+               if (!(irq_arg & 0xFFFF)) {
+                       PERROR("No mask specified.\n");
+                       return ME_ERRNO_INVALID_IRQ_ARG;
+               }
+       } else {
+               PERROR("Invalid irq source specified.\n");
+               return ME_ERRNO_INVALID_IRQ_SOURCE;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
+               outw(irq_arg, instance->pattern_reg);
+               instance->compare_value = irq_arg;
+               instance->filtering_flag =
+                   (flags & ME_IO_IRQ_START_PATTERN_FILTERING) ? 1 : 0;
+       }
+       if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               outw(irq_arg, instance->mask_reg);
+       }
+
+       spin_lock(instance->ctrl_reg_lock);
+       ctrl = inw(instance->ctrl_reg);
+       ctrl |= ME8100_DIO_CTRL_BIT_INTB_0;
+       if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
+               ctrl &= ~ME8100_DIO_CTRL_BIT_INTB_1;
+       }
+
+       if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               ctrl |= ME8100_DIO_CTRL_BIT_INTB_1;
+       }
+       outw(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       instance->rised = 0;
+       instance->status_value = 0;
+       instance->status_value_edges = 0;
+       instance->line_value = inw(instance->port_reg);
+       instance->status_flag = flags & ME_IO_IRQ_START_EXTENDED_STATUS;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_di_io_irq_wait(me_subdevice_t * subdevice,
+                                struct file *filep,
+                                int channel,
+                                int *irq_count,
+                                int *value, int time_out, int flags)
+{
+       me8100_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       unsigned long cpu_flags;
+       int count;
+
+       PDEBUG("executed.\n");
+       PDEVELOP("PID: %d.\n", current->pid);
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       if (flags &
+           ~(ME_IO_IRQ_WAIT_NORMAL_STATUS | ME_IO_IRQ_WAIT_EXTENDED_STATUS)) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->rised <= 0) {
+               instance->rised = 0;
+               count = instance->irq_count;
+
+               if (time_out) {
+                       t = wait_event_interruptible_timeout(instance->
+                                                            wait_queue,
+                                                            ((count !=
+                                                              instance->
+                                                              irq_count)
+                                                             || (instance->
+                                                                 rised < 0)),
+                                                            t);
+//                      t = wait_event_interruptible_timeout(instance->wait_queue, (instance->rised != 0), t);
+                       if (t == 0) {
+                               PERROR("Wait on interrupt timed out.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               } else {
+                       wait_event_interruptible(instance->wait_queue,
+                                                ((count != instance->irq_count)
+                                                 || (instance->rised < 0)));
+//                      wait_event_interruptible(instance->wait_queue, (instance->rised != 0));
+               }
+
+               if (instance->rised < 0) {
+                       PERROR("Wait on interrupt aborted by user.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+       }
+
+       if (signal_pending(current)) {
+               PERROR("Wait on interrupt aborted by signal.\n");
+               err = ME_ERRNO_SIGNAL;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       *irq_count = instance->irq_count;
+       if (!err) {
+               if (flags & ME_IO_IRQ_WAIT_NORMAL_STATUS) {
+                       *value = instance->status_value;
+               } else if (flags & ME_IO_IRQ_WAIT_EXTENDED_STATUS) {
+                       *value = instance->status_value_edges;
+               } else {        // Use default
+                       if (!instance->status_flag) {
+                               *value = instance->status_value;
+                       } else {
+                               *value = instance->status_value_edges;
+                       }
+               }
+               instance->rised = 0;
+/*
+                       instance->status_value = 0;
+                       instance->status_value_edges = 0;
+*/
+       } else {
+               *value = 0;
+       }
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_di_io_irq_stop(me_subdevice_t * subdevice,
+                                struct file *filep, int channel, int flags)
+{
+       me8100_di_subdevice_t *instance;
+       uint16_t ctrl;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->ctrl_reg_lock);
+       ctrl = inw(instance->ctrl_reg);
+       ctrl &= ~(ME8100_DIO_CTRL_BIT_INTB_1 | ME8100_DIO_CTRL_BIT_INTB_0);
+       outw(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock(instance->ctrl_reg_lock);
+       instance->rised = -1;
+       instance->status_value = 0;
+       instance->status_value_edges = 0;
+       instance->filtering_flag = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_di_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me8100_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_WORD:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                       } else {
+                               PERROR
+                                   ("Invalid port configuration specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_di_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me8100_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+
+       switch (flags) {
+
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 16)) {
+                       *value = inw(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inw(instance->port_reg) & 0xFF;
+               } else if (channel == 1) {
+                       *value = (inw(instance->port_reg) >> 8) & 0xFF;
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if (channel == 0) {
+                       *value = inw(instance->port_reg);
+               } else {
+                       PERROR("Invalid word number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_di_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 16;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_di_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DI;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_BIT_PATTERN_IRQ | ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_ANY;
+       return ME_ERRNO_SUCCESS;
+}
+
+static void me8100_di_destructor(struct me_subdevice *subdevice)
+{
+       me8100_di_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) subdevice;
+
+       free_irq(instance->irq, (void *)instance);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me8100_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me8100_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       me8100_di_subdevice_t *instance;
+       uint32_t icsr;
+
+       uint16_t irq_status;
+       uint16_t line_value = 0;
+
+       uint32_t status_val = 0;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_di_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       icsr = inl(instance->irq_status_reg);
+       if (instance->di_idx == 0) {
+
+               if ((icsr &
+                    (PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_PCI_INT_EN |
+                     PLX_INTCSR_LOCAL_INT1_EN)) !=
+                   (PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_PCI_INT_EN |
+                    PLX_INTCSR_LOCAL_INT1_EN)) {
+                       PINFO
+                           ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
+                            jiffies, __FUNCTION__, icsr);
+                       return IRQ_NONE;
+               }
+       } else if (instance->di_idx == 1) {
+               if ((icsr &
+                    (PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_PCI_INT_EN |
+                     PLX_INTCSR_LOCAL_INT2_EN)) !=
+                   (PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_PCI_INT_EN |
+                    PLX_INTCSR_LOCAL_INT2_EN)) {
+                       PINFO
+                           ("%ld Shared interrupt. %s(): idx=1 plx:irq_status_reg=0x%04X\n",
+                            jiffies, __FUNCTION__, icsr);
+                       return IRQ_NONE;
+               }
+       } else {
+               PERROR("%s():Wrong interrupt idx=%d csr=0x%X.\n", __FUNCTION__,
+                      instance->di_idx, icsr);
+               return IRQ_NONE;
+       }
+
+       PDEBUG("me8100_isr():Interrupt from idx=%d occured.\n",
+              instance->di_idx);
+       spin_lock(&instance->subdevice_lock);
+       inw(instance->irq_reset_reg);
+       line_value = inw(instance->port_reg);
+
+       irq_status = instance->line_value ^ line_value;
+
+       // Make extended information.
+       status_val |= (0x00FF & (~(uint16_t) instance->line_value & line_value)) << 16; //Raise
+       status_val |= (0x00FF & ((uint16_t) instance->line_value & ~line_value));       //Fall
+
+       instance->line_value = line_value;
+
+       if (instance->rised == 0) {
+               instance->status_value = irq_status;
+               instance->status_value_edges = status_val;
+       } else {
+               instance->status_value |= irq_status;
+               instance->status_value_edges |= status_val;
+       }
+
+       if (instance->filtering_flag) { // For compare mode only.
+               if (instance->compare_value == instance->line_value) {
+                       instance->rised = 1;
+                       instance->irq_count++;
+               }
+       } else {
+               instance->rised = 1;
+               instance->irq_count++;
+       }
+
+       spin_unlock(&instance->subdevice_lock);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+me8100_di_subdevice_t *me8100_di_constructor(uint32_t me8100_reg_base,
+                                            uint32_t plx_reg_base,
+                                            unsigned int di_idx,
+                                            int irq,
+                                            spinlock_t * ctrl_reg_lock)
+{
+       me8100_di_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me8100_di_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8100_di_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the subdevice index. */
+       subdevice->di_idx = di_idx;
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       /* Register interrupt service routine. */
+       subdevice->irq = irq;
+       err = request_irq(subdevice->irq, me8100_isr,
+#ifdef IRQF_DISABLED
+                         IRQF_DISABLED | IRQF_SHARED,
+#else
+                         SA_INTERRUPT | SA_SHIRQ,
+#endif
+                         ME8100_NAME, (void *)subdevice);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       PINFO("Registered irq=%d.\n", subdevice->irq);
+
+       /* Initialize the registers */
+       subdevice->ctrl_reg =
+           me8100_reg_base + ME8100_CTRL_REG_A + di_idx * ME8100_REG_OFFSET;
+       subdevice->port_reg =
+           me8100_reg_base + ME8100_DI_REG_A + di_idx * ME8100_REG_OFFSET;
+       subdevice->mask_reg =
+           me8100_reg_base + ME8100_MASK_REG_A + di_idx * ME8100_REG_OFFSET;
+       subdevice->pattern_reg =
+           me8100_reg_base + ME8100_PATTERN_REG_A + di_idx * ME8100_REG_OFFSET;
+       subdevice->din_int_reg =
+           me8100_reg_base + ME8100_INT_DI_REG_A + di_idx * ME8100_REG_OFFSET;
+       subdevice->irq_reset_reg =
+           me8100_reg_base + ME8100_RES_INT_REG_A + di_idx * ME8100_REG_OFFSET;
+       subdevice->irq_status_reg = plx_reg_base + PLX_INTCSR;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = me8100_reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_irq_start = me8100_di_io_irq_start;
+       subdevice->base.me_subdevice_io_irq_wait = me8100_di_io_irq_wait;
+       subdevice->base.me_subdevice_io_irq_stop = me8100_di_io_irq_stop;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8100_di_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me8100_di_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8100_di_io_single_read;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8100_di_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8100_di_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8100_di_query_subdevice_caps;
+       subdevice->base.me_subdevice_destructor = me8100_di_destructor;
+
+       subdevice->rised = 0;
+       subdevice->irq_count = 0;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me8100_di.h b/drivers/staging/meilhaus/me8100_di.h
new file mode 100644 (file)
index 0000000..e1db791
--- /dev/null
@@ -0,0 +1,89 @@
+/**
+ * @file me8100_di.h
+ *
+ * @brief ME-8100 digital input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8100_DI_H_
+#define _ME8100_DI_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me8100_di_subdevice {
+       // Inheritance
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;
+
+       unsigned di_idx;
+
+       int irq;
+       volatile int rised;
+       unsigned int irq_count;
+
+       uint status_flag;                               /**< Default interupt status flag */
+       uint status_value;                              /**< Interupt status */
+       uint status_value_edges;                /**< Extended interupt status */
+       uint line_value;
+
+       uint16_t compare_value;
+       uint8_t filtering_flag;
+
+       wait_queue_head_t wait_queue;
+
+       unsigned long ctrl_reg;
+       unsigned long port_reg;
+       unsigned long mask_reg;
+       unsigned long pattern_reg;
+       unsigned long long din_int_reg;
+       unsigned long irq_reset_reg;
+       unsigned long irq_status_reg;
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+
+} me8100_di_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-8100 digital input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8100_di_subdevice_t *me8100_di_constructor(uint32_t me8100_reg_base,
+                                            uint32_t plx_reg_base,
+                                            unsigned int di_idx,
+                                            int irq,
+                                            spinlock_t * ctrl_leg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8100_di_reg.h b/drivers/staging/meilhaus/me8100_di_reg.h
new file mode 100644 (file)
index 0000000..063bd19
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * @file me8100_di_reg.h
+ *
+ * @brief ME-8100 digital input subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8100_DI_REG_H_
+#define _ME8100_DI_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8100_RES_INT_REG_A           0x02    //(r, )
+#define ME8100_DI_REG_A                        0x04    //(r, )
+#define ME8100_PATTERN_REG_A           0x08    //( ,w)
+#define ME8100_MASK_REG_A              0x0A    //( ,w)
+#define ME8100_INT_DI_REG_A            0x0A    //(r, )
+
+#define ME8100_RES_INT_REG_B           0x0E    //(r, )
+#define ME8100_DI_REG_B                        0x10    //(r, )
+#define ME8100_PATTERN_REG_B           0x14    //( ,w)
+#define ME8100_MASK_REG_B              0x16    //( ,w)
+#define ME8100_INT_DI_REG_B            0x16    //(r, )
+
+#define ME8100_REG_OFFSET              0x0C
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8100_do.c b/drivers/staging/meilhaus/me8100_do.c
new file mode 100644 (file)
index 0000000..957b9f9
--- /dev/null
@@ -0,0 +1,391 @@
+/**
+ * @file me8100_do.c
+ *
+ * @brief ME-8100 digital output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me8100_reg.h"
+#include "me8100_do_reg.h"
+#include "me8100_do.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me8100_do_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me8100_do_subdevice_t *instance;
+       uint16_t ctrl;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_do_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       ctrl = inw(instance->ctrl_reg);
+       ctrl &= ME8100_DIO_CTRL_BIT_INTB_1 | ME8100_DIO_CTRL_BIT_INTB_0;
+       outw(ctrl, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock(instance->ctrl_reg_lock);
+       outw(0, instance->port_reg);
+       instance->port_reg_mirror = 0;
+       PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_do_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me8100_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       int config;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       config = inw(instance->ctrl_reg);
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_WORD:
+               if (channel == 0) {
+                       if (single_config ==
+                           ME_SINGLE_CONFIG_DIO_HIGH_IMPEDANCE) {
+                               config &= ~(ME8100_DIO_CTRL_BIT_ENABLE_DIO);
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_SINK) {
+                               config |= ME8100_DIO_CTRL_BIT_ENABLE_DIO;
+                               config &= ~ME8100_DIO_CTRL_BIT_SOURCE;
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_SOURCE) {
+                               config |=
+                                   ME8100_DIO_CTRL_BIT_ENABLE_DIO |
+                                   ME8100_DIO_CTRL_BIT_SOURCE;
+                       } else {
+                               PERROR
+                                   ("Invalid port configuration specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid word number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outw(config, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, config);
+       }
+
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_do_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me8100_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 16)) {
+                       *value = instance->port_reg_mirror & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = instance->port_reg_mirror & 0xFF;
+               } else if (channel == 1) {
+                       *value = (instance->port_reg_mirror >> 8) & 0xFF;
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if (channel == 0) {
+                       *value = instance->port_reg_mirror;
+               } else {
+                       PERROR("Invalid word number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_do_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me8100_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8100_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 16)) {
+                       instance->port_reg_mirror =
+                           value ? (instance->
+                                    port_reg_mirror | (0x1 << channel))
+                           : (instance->port_reg_mirror & ~(0x1 << channel));
+                       outw(instance->port_reg_mirror, instance->port_reg);
+                       PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->port_reg - instance->reg_base,
+                                  instance->port_reg_mirror);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       instance->port_reg_mirror &= ~0xFF;
+                       instance->port_reg_mirror |= value & 0xFF;
+                       outw(instance->port_reg_mirror, instance->port_reg);
+                       PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->port_reg - instance->reg_base,
+                                  instance->port_reg_mirror);
+               } else if (channel == 1) {
+                       instance->port_reg_mirror &= ~0xFF00;
+                       instance->port_reg_mirror |= (value << 8) & 0xFF00;
+                       outw(instance->port_reg_mirror, instance->port_reg);
+                       PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->port_reg - instance->reg_base,
+                                  instance->port_reg_mirror);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_WORD:
+               if (channel == 0) {
+                       instance->port_reg_mirror = value;
+                       outw(value, instance->port_reg);
+                       PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->port_reg - instance->reg_base,
+                                  value);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8100_do_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 16;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_do_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_SINK_SOURCE;
+       return ME_ERRNO_SUCCESS;
+}
+
+me8100_do_subdevice_t *me8100_do_constructor(uint32_t reg_base,
+                                            unsigned int do_idx,
+                                            spinlock_t * ctrl_reg_lock)
+{
+       me8100_do_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me8100_do_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8100_do_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+
+       /* Initialize registers */
+       if (do_idx == 0) {
+               subdevice->port_reg = reg_base + ME8100_DO_REG_A;
+               subdevice->ctrl_reg = reg_base + ME8100_CTRL_REG_A;
+       } else if (do_idx == 1) {
+               subdevice->port_reg = reg_base + ME8100_DO_REG_B;
+               subdevice->ctrl_reg = reg_base + ME8100_CTRL_REG_B;
+       } else {
+               PERROR("Wrong subdevice idx=%d.\n", do_idx);
+               kfree(subdevice);
+               return NULL;
+       }
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the subdevice index */
+       subdevice->do_idx = do_idx;
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8100_do_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me8100_do_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8100_do_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me8100_do_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8100_do_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8100_do_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8100_do_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me8100_do.h b/drivers/staging/meilhaus/me8100_do.h
new file mode 100644 (file)
index 0000000..acf8801
--- /dev/null
@@ -0,0 +1,70 @@
+/**
+ * @file me8100_do.h
+ *
+ * @brief ME-8100 digital output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8100_DO_H_
+#define _ME8100_DO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me8100_do_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect the #ctrl_reg. */
+
+       unsigned int do_idx;
+
+       uint16_t port_reg_mirror;               /**< Mirror used to store current port register setting which is write only. */
+
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Control register. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me8100_do_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-8100 digital output subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param do_idx The index of the digital output subdevice on this device.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8100_do_subdevice_t *me8100_do_constructor(uint32_t reg_base,
+                                            unsigned int do_idx,
+                                            spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8100_do_reg.h b/drivers/staging/meilhaus/me8100_do_reg.h
new file mode 100644 (file)
index 0000000..13a2380
--- /dev/null
@@ -0,0 +1,36 @@
+/**
+ * @file me8100_ao_reg.h
+ *
+ * @brief ME-8100 analog output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8100_DO_REG_H_
+#define _ME8100_DO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8100_DO_REG_A                        0x06    //( ,w)
+#define ME8100_DO_REG_B                        0x12    //( ,w)
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8100_reg.h b/drivers/staging/meilhaus/me8100_reg.h
new file mode 100644 (file)
index 0000000..d8c4b1c
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * @file me8100_reg.h
+ *
+ * @brief ME-8100 register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8100_REG_H_
+#define _ME8100_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8100_CTRL_REG_A                      0x00    //( ,w)
+#define ME8100_CTRL_REG_B                      0x0C    //( ,w)
+
+#define ME8100_DIO_CTRL_BIT_SOURCE             0x10
+#define ME8100_DIO_CTRL_BIT_INTB_1             0x20
+#define ME8100_DIO_CTRL_BIT_INTB_0             0x40
+#define ME8100_DIO_CTRL_BIT_ENABLE_DIO         0x80
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_device.c b/drivers/staging/meilhaus/me8200_device.c
new file mode 100644 (file)
index 0000000..261c0cb
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ * @file me8200_device.c
+ *
+ * @brief ME-8200 device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include "meids.h"
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "meplx_reg.h"
+#include "medevice.h"
+#include "me8200_device.h"
+#include "mesubdevice.h"
+#include "me8200_di.h"
+#include "me8200_do.h"
+#include "me8200_dio.h"
+
+me_device_t *me8200_pci_constructor(struct pci_dev *pci_device)
+{
+       me8200_device_t *me8200_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       me8200_device = kmalloc(sizeof(me8200_device_t), GFP_KERNEL);
+
+       if (!me8200_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(me8200_device, 0, sizeof(me8200_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) me8200_device, pci_device);
+
+       if (err) {
+               kfree(me8200_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           me8200_versions_get_device_index(me8200_device->base.info.pci.
+                                            device_id);
+
+       // Initialize spin lock .
+       spin_lock_init(&me8200_device->irq_ctrl_lock);
+       spin_lock_init(&me8200_device->irq_mode_lock);
+       spin_lock_init(&me8200_device->dio_ctrl_lock);
+
+       /* Setup the PLX interrupt configuration */
+       outl(PLX_INTCSR_LOCAL_INT1_EN |
+            PLX_INTCSR_LOCAL_INT1_POL |
+            PLX_INTCSR_LOCAL_INT2_EN |
+            PLX_INTCSR_LOCAL_INT2_POL |
+            PLX_INTCSR_PCI_INT_EN,
+            me8200_device->base.info.pci.reg_bases[1] + PLX_INTCSR);
+
+       // Create subdevice instances.
+
+       for (i = 0; i < me8200_versions[version_idx].di_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8200_di_constructor(me8200_device->
+                                                            base.info.pci.
+                                                            reg_bases[2], i,
+                                                            me8200_device->
+                                                            base.irq,
+                                                            &me8200_device->
+                                                            irq_ctrl_lock,
+                                                            &me8200_device->
+                                                            irq_mode_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me8200_device);
+                       kfree(me8200_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me8200_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me8200_versions[version_idx].do_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8200_do_constructor(me8200_device->
+                                                            base.info.pci.
+                                                            reg_bases[2], i,
+                                                            me8200_device->
+                                                            base.irq,
+                                                            &me8200_device->
+                                                            irq_mode_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me8200_device);
+                       kfree(me8200_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me8200_device->base.slist,
+                                           subdevice);
+       }
+
+       for (i = 0; i < me8200_versions[version_idx].dio_subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) me8200_dio_constructor(me8200_device->
+                                                             base.info.pci.
+                                                             reg_bases[2], i,
+                                                             &me8200_device->
+                                                             dio_ctrl_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) me8200_device);
+                       kfree(me8200_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&me8200_device->base.slist,
+                                           subdevice);
+       }
+
+       return (me_device_t *) me8200_device;
+}
+
+// Init and exit of module.
+
+static int __init me8200_init(void)
+{
+       PDEBUG("executed.\n.");
+       return 0;
+}
+
+static void __exit me8200_exit(void)
+{
+       PDEBUG("executed.\n.");
+}
+
+module_init(me8200_init);
+
+module_exit(me8200_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for Template Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus Template Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(me8200_pci_constructor);
diff --git a/drivers/staging/meilhaus/me8200_device.h b/drivers/staging/meilhaus/me8200_device.h
new file mode 100644 (file)
index 0000000..cbd2a01
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * @file me8200_device.h
+ *
+ * @brief ME-8200 device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DEVICE_H
+#define _ME8200_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding ME-8200 device capabilities.
+ */
+typedef struct me8200_version {
+       uint16_t device_id;
+       unsigned int di_subdevices;
+       unsigned int do_subdevices;
+       unsigned int dio_subdevices;
+} me8200_version_t;
+
+/**
+ * @brief Device capabilities.
+ */
+static me8200_version_t me8200_versions[] = {
+       {PCI_DEVICE_ID_MEILHAUS_ME8200_A, 1, 1, 2},
+       {PCI_DEVICE_ID_MEILHAUS_ME8200_B, 2, 2, 2},
+       {0},
+};
+
+#define ME8200_DEVICE_VERSIONS (sizeof(me8200_versions) / sizeof(me8200_version_t) - 1)        /**< Returns the number of entries in #me8200_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #me8200_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #me8200_versions.
+ */
+static inline unsigned int me8200_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < ME8200_DEVICE_VERSIONS; i++)
+               if (me8200_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The ME-8200 device class structure.
+ */
+typedef struct me8200_device {
+       me_device_t base;               /**< The Meilhaus device base class. */
+
+       /* Child class attributes. */
+       spinlock_t irq_ctrl_lock;       /**< Lock for the interrupt control register. */
+       spinlock_t irq_mode_lock;       /**< Lock for the interrupt mode register. */
+       spinlock_t dio_ctrl_lock;       /**< Lock for the digital i/o control register. */
+} me8200_device_t;
+
+/**
+ * @brief The ME-8200 device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new ME-8200 device instance. \n
+ *         NULL on error.
+ */
+me_device_t *me8200_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_di.c b/drivers/staging/meilhaus/me8200_di.c
new file mode 100644 (file)
index 0000000..0bb4567
--- /dev/null
@@ -0,0 +1,857 @@
+/**
+ * @file me8200_di.c
+ *
+ * @brief ME-8200 digital input subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+///Includes
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/version.h>
+
+#include "medefines.h"
+#include "meerror.h"
+
+#include "meids.h"
+#include "medebug.h"
+#include "me8200_reg.h"
+#include "me8200_di_reg.h"
+#include "me8200_di.h"
+
+/// Defines
+static void me8200_di_destructor(struct me_subdevice *subdevice);
+static int me8200_di_io_irq_start(me_subdevice_t * subdevice,
+                                 struct file *filep,
+                                 int channel,
+                                 int irq_source,
+                                 int irq_edge, int irq_arg, int flags);
+static int me8200_di_io_irq_wait(me_subdevice_t * subdevice,
+                                struct file *filep,
+                                int channel,
+                                int *irq_count,
+                                int *value, int time_out, int flags);
+static int me8200_di_io_irq_stop(me_subdevice_t * subdevice,
+                                struct file *filep, int channel, int flags);
+static int me8200_di_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags);
+static int me8200_di_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags);
+static int me8200_di_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags);
+static int me8200_di_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number);
+static int me8200_di_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype);
+static int me8200_di_query_subdevice_caps(me_subdevice_t * subdevice,
+                                         int *caps);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me8200_isr(int irq, void *dev_id);
+#else
+static irqreturn_t me8200_isr(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me8200_isr_EX(int irq, void *dev_id);
+#else
+static irqreturn_t me8200_isr_EX(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+static void me8200_di_check_version(me8200_di_subdevice_t * instance,
+                                   unsigned long addr);
+
+///Functions
+static int me8200_di_io_irq_start(me_subdevice_t * subdevice,
+                                 struct file *filep,
+                                 int channel,
+                                 int irq_source,
+                                 int irq_edge, int irq_arg, int flags)
+{
+       me8200_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       volatile uint8_t tmp;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_di_subdevice_t *) subdevice;
+
+       if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
+               if (flags &
+                   ~(ME_IO_IRQ_START_PATTERN_FILTERING |
+                     ME_IO_IRQ_START_DIO_BYTE)) {
+                       PERROR("Invalid flag specified.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+
+               if (irq_edge != ME_IRQ_EDGE_NOT_USED) {
+                       PERROR("Invalid irq edge specified.\n");
+                       return ME_ERRNO_INVALID_IRQ_EDGE;
+               }
+       } else if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               if (flags &
+                   ~(ME_IO_IRQ_START_EXTENDED_STATUS |
+                     ME_IO_IRQ_START_DIO_BYTE)) {
+                       PERROR("Invalid flag specified.\n");
+                       return ME_ERRNO_INVALID_FLAGS;
+               }
+
+               if ((irq_edge != ME_IRQ_EDGE_RISING)
+                   && (irq_edge != ME_IRQ_EDGE_FALLING)
+                   && (irq_edge != ME_IRQ_EDGE_ANY)) {
+                       PERROR("Invalid irq edge specified.\n");
+                       return ME_ERRNO_INVALID_IRQ_EDGE;
+               }
+
+               if (!(irq_arg & 0xFF)) {
+                       PERROR("No mask specified.\n");
+                       return ME_ERRNO_INVALID_IRQ_ARG;
+               }
+       } else {
+               PERROR("Invalid irq source specified.\n");
+               return ME_ERRNO_INVALID_IRQ_SOURCE;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+       if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
+               outb(irq_arg, instance->compare_reg);
+               PDEBUG_REG("compare_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->compare_reg - instance->reg_base, irq_arg);
+               outb(0xFF, instance->mask_reg);
+               PDEBUG_REG("mask_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->mask_reg - instance->reg_base, 0xff);
+               instance->compare_value = irq_arg;
+               instance->filtering_flag =
+                   (flags & ME_IO_IRQ_START_PATTERN_FILTERING) ? 1 : 0;
+       }
+       if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               outb(irq_arg, instance->mask_reg);
+               PDEBUG_REG("mask_reg outb(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->mask_reg - instance->reg_base, irq_arg);
+               instance->filtering_flag = 0;
+       }
+
+       spin_lock(instance->irq_mode_lock);
+       tmp = inb(instance->irq_mode_reg);
+       tmp &=
+           ~(ME8200_IRQ_MODE_MASK <<
+             (ME8200_IRQ_MODE_DI_SHIFT * instance->di_idx));
+       if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
+               tmp |=
+                   ME8200_IRQ_MODE_MASK_COMPARE << (ME8200_IRQ_MODE_DI_SHIFT *
+                                                    instance->di_idx);
+       }
+
+       if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               tmp |=
+                   ME8200_IRQ_MODE_MASK_MASK << (ME8200_IRQ_MODE_DI_SHIFT *
+                                                 instance->di_idx);
+       }
+       outb(tmp, instance->irq_mode_reg);
+       PDEBUG_REG("irq_mode_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_mode_reg - instance->reg_base, tmp);
+       spin_unlock(instance->irq_mode_lock);
+
+       spin_lock(instance->irq_ctrl_lock);
+       tmp = inb(instance->irq_ctrl_reg);
+       tmp |=
+           (ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
+            (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+       tmp |=
+           ME8200_DI_IRQ_CTRL_BIT_ENABLE << (ME8200_DI_IRQ_CTRL_SHIFT *
+                                             instance->di_idx);
+
+       if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
+               tmp &=
+                   ~(ME8200_DI_IRQ_CTRL_MASK_EDGE <<
+                     (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+               if (irq_edge == ME_IRQ_EDGE_RISING) {
+                       tmp |=
+                           ME8200_DI_IRQ_CTRL_MASK_EDGE_RISING <<
+                           (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx);
+               } else if (irq_edge == ME_IRQ_EDGE_FALLING) {
+                       tmp |=
+                           ME8200_DI_IRQ_CTRL_MASK_EDGE_FALLING <<
+                           (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx);
+               } else if (irq_edge == ME_IRQ_EDGE_ANY) {
+                       tmp |=
+                           ME8200_DI_IRQ_CTRL_MASK_EDGE_ANY <<
+                           (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx);
+               }
+       }
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+       tmp &=
+           ~(ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
+             (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+
+       instance->line_value = inb(instance->port_reg);
+       spin_unlock(instance->irq_ctrl_lock);
+
+       instance->rised = 0;
+       instance->status_value = 0;
+       instance->status_value_edges = 0;
+       instance->status_flag = flags & ME_IO_IRQ_START_EXTENDED_STATUS;
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_di_io_irq_wait(me_subdevice_t * subdevice,
+                                struct file *filep,
+                                int channel,
+                                int *irq_count,
+                                int *value, int time_out, int flags)
+{
+       me8200_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       unsigned long cpu_flags;
+       int count;
+
+       PDEBUG("executed.\n");
+       PDEVELOP("PID: %d.\n", current->pid);
+
+       instance = (me8200_di_subdevice_t *) subdevice;
+
+       if (flags &
+           ~(ME_IO_IRQ_WAIT_NORMAL_STATUS | ME_IO_IRQ_WAIT_EXTENDED_STATUS)) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->rised <= 0) {
+               instance->rised = 0;
+               count = instance->count;
+
+               if (time_out) {
+                       t = wait_event_interruptible_timeout(instance->
+                                                            wait_queue,
+                                                            ((count !=
+                                                              instance->count)
+                                                             || (instance->
+                                                                 rised < 0)),
+                                                            t);
+//                      t = wait_event_interruptible_timeout(instance->wait_queue, (instance->rised != 0), t);
+                       if (t == 0) {
+                               PERROR("Wait on interrupt timed out.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               } else {
+                       wait_event_interruptible(instance->wait_queue,
+                                                ((count != instance->count)
+                                                 || (instance->rised < 0)));
+//                      wait_event_interruptible(instance->wait_queue, (instance->rised != 0));
+               }
+
+               if (instance->rised < 0) {
+                       PERROR("Wait on interrupt aborted by user.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+       }
+
+       if (signal_pending(current)) {
+               PERROR("Wait on interrupt aborted by signal.\n");
+               err = ME_ERRNO_SIGNAL;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       *irq_count = instance->count;
+       if (!err) {
+               if (flags & ME_IO_IRQ_WAIT_NORMAL_STATUS) {
+                       *value = instance->status_value;
+               } else if (flags & ME_IO_IRQ_WAIT_EXTENDED_STATUS) {
+                       *value = instance->status_value_edges;
+               } else {        // Use default
+                       if (!instance->status_flag) {
+                               *value = instance->status_value;
+                       } else {
+                               *value = instance->status_value_edges;
+                       }
+               }
+               instance->rised = 0;
+/*
+                       instance->status_value = 0;
+                       instance->status_value_edges = 0;
+*/
+       } else {
+               *value = 0;
+       }
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_di_io_irq_stop(me_subdevice_t * subdevice,
+                                struct file *filep, int channel, int flags)
+{
+       me8200_di_subdevice_t *instance;
+       uint8_t tmp;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_di_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER spin_lock_irqsave(&instance->subdevice_lock, status);
+       spin_lock(instance->irq_ctrl_lock);
+       tmp = inb(instance->irq_ctrl_reg);
+       tmp |=
+           (ME8200_DI_IRQ_CTRL_BIT_ENABLE <<
+            (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+       tmp &=
+           ~(ME8200_DI_IRQ_CTRL_BIT_ENABLE <<
+             (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+       tmp |=
+           (ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
+            (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+//                      tmp &= ~(ME8200_DI_IRQ_CTRL_BIT_CLEAR << (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->irq_ctrl_lock);
+
+       instance->rised = -1;
+       instance->status_value = 0;
+       instance->status_value_edges = 0;
+       instance->filtering_flag = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_di_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me8200_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                       } else {
+                               PERROR("Invalid port direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_di_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me8200_di_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_di_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inb(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_di_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me8200_di_subdevice_t *instance = (me8200_di_subdevice_t *) subdevice;
+
+       PDEBUG("executed.\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       instance->count = 0;
+       return me8200_di_io_irq_stop(subdevice, filep, 0, 0);
+}
+
+static int me8200_di_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_di_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DI;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps =
+           ME_CAPS_DIO_BIT_PATTERN_IRQ |
+           ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_RISING |
+           ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_FALLING |
+           ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_ANY;
+       return ME_ERRNO_SUCCESS;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me8200_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me8200_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       me8200_di_subdevice_t *instance;
+       uint8_t ctrl;
+       uint8_t irq_status;
+       uint8_t line_value = 0;
+       uint8_t line_status = 0;
+       uint32_t status_val = 0;
+
+       instance = (me8200_di_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       irq_status = inb(instance->irq_status_reg);
+       if (!irq_status) {
+               PINFO
+                   ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n",
+                    jiffies, __FUNCTION__, instance->di_idx, irq_status);
+               return IRQ_NONE;
+       }
+
+       PDEBUG("executed.\n");
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->irq_ctrl_lock);
+       ctrl = inb(instance->irq_ctrl_reg);
+       ctrl |=
+           ME8200_DI_IRQ_CTRL_BIT_CLEAR << (ME8200_DI_IRQ_CTRL_SHIFT *
+                                            instance->di_idx);
+       outb(ctrl, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, ctrl);
+       ctrl &=
+           ~(ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
+             (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
+       outb(ctrl, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, ctrl);
+
+       line_value = inb(instance->port_reg);
+       spin_unlock(instance->irq_ctrl_lock);
+
+       line_status = ((uint8_t) instance->line_value ^ line_value);
+
+       // Make extended information.
+       status_val |= (0x00FF & (~(uint8_t) instance->line_value & line_value)) << 16;  //Raise
+       status_val |= (0x00FF & ((uint8_t) instance->line_value & ~line_value));        //Fall
+
+       instance->line_value = (int)line_value;
+
+       if (instance->rised == 0) {
+               instance->status_value = irq_status | line_status;
+               instance->status_value_edges = status_val;
+       } else {
+               instance->status_value |= irq_status | line_status;
+               instance->status_value_edges |= status_val;
+       }
+
+       if (instance->filtering_flag) { // For compare mode only.
+               if (instance->compare_value == instance->line_value) {
+                       instance->rised = 1;
+                       instance->count++;
+               }
+       } else {
+               instance->rised = 1;
+               instance->count++;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       spin_unlock(&instance->subdevice_lock);
+
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me8200_isr_EX(int irq, void *dev_id)
+#else
+static irqreturn_t me8200_isr_EX(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       me8200_di_subdevice_t *instance;
+       uint8_t irq_status = 0;
+       uint16_t irq_status_EX = 0;
+       uint32_t status_val = 0;
+       int i, j;
+
+       instance = (me8200_di_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       PDEBUG("executed.\n");
+
+       //Reset latches. Copy status to extended registers.
+       irq_status = inb(instance->irq_status_reg);
+       PDEBUG_REG("idx=%d irq_status_reg=0x%02X\n", instance->di_idx,
+                  irq_status);
+
+       if (!irq_status) {
+               PINFO
+                   ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n",
+                    jiffies, __FUNCTION__, instance->di_idx, irq_status);
+               return IRQ_NONE;
+       }
+
+       irq_status_EX = inb(instance->irq_status_low_reg);
+       irq_status_EX |= (inb(instance->irq_status_high_reg) << 8);
+
+       PDEVELOP("EXTENDED REG: 0x%04x\n", irq_status_EX);
+       instance->line_value = inb(instance->port_reg);
+
+       // Format extended information.
+       for (i = 0, j = 0; i < 8; i++, j += 2) {
+               status_val |= ((0x01 << j) & irq_status_EX) >> (j - i); //Fall
+               status_val |= ((0x01 << (j + 1)) & irq_status_EX) << (15 - j + i);      //Raise
+       }
+
+       spin_lock(&instance->subdevice_lock);
+       if (instance->rised == 0) {
+               instance->status_value = irq_status;
+               instance->status_value_edges = status_val;
+       } else {
+               instance->status_value |= irq_status;
+               instance->status_value_edges |= status_val;
+       }
+
+       if (instance->filtering_flag) { // For compare mode only.
+               if (instance->compare_value == instance->line_value) {
+                       instance->rised = 1;
+                       instance->count++;
+               }
+       } else {
+               instance->rised = 1;
+               instance->count++;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+static void me8200_di_destructor(struct me_subdevice *subdevice)
+{
+       me8200_di_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_di_subdevice_t *) subdevice;
+
+       free_irq(instance->irq, (void *)instance);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+me8200_di_subdevice_t *me8200_di_constructor(uint32_t me8200_regbase,
+                                            unsigned int di_idx,
+                                            int irq,
+                                            spinlock_t * irq_ctrl_lock,
+                                            spinlock_t * irq_mode_lock)
+{
+       me8200_di_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me8200_di_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8200_di_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Check firmware version.
+       me8200_di_check_version(subdevice,
+                               me8200_regbase + ME8200_FIRMWARE_VERSION_REG);
+
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->irq_ctrl_lock = irq_ctrl_lock;
+       subdevice->irq_mode_lock = irq_mode_lock;
+
+       /* Save the subdevice index. */
+       subdevice->di_idx = di_idx;
+
+       /* Initialize registers */
+       if (di_idx == 0) {
+               subdevice->port_reg = me8200_regbase + ME8200_DI_PORT_0_REG;
+               subdevice->mask_reg = me8200_regbase + ME8200_DI_MASK_0_REG;
+               subdevice->compare_reg =
+                   me8200_regbase + ME8200_DI_COMPARE_0_REG;
+               subdevice->irq_status_reg =
+                   me8200_regbase + ME8200_DI_CHANGE_0_REG;
+
+               subdevice->irq_status_low_reg =
+                   me8200_regbase + ME8200_DI_EXTEND_CHANGE_0_LOW_REG;
+               subdevice->irq_status_high_reg =
+                   me8200_regbase + ME8200_DI_EXTEND_CHANGE_0_HIGH_REG;
+       } else if (di_idx == 1) {
+               subdevice->port_reg = me8200_regbase + ME8200_DI_PORT_1_REG;
+               subdevice->mask_reg = me8200_regbase + ME8200_DI_MASK_1_REG;
+               subdevice->compare_reg =
+                   me8200_regbase + ME8200_DI_COMPARE_1_REG;
+               subdevice->irq_status_reg =
+                   me8200_regbase + ME8200_DI_CHANGE_1_REG;
+
+               subdevice->irq_status_low_reg =
+                   me8200_regbase + ME8200_DI_EXTEND_CHANGE_1_LOW_REG;
+               subdevice->irq_status_high_reg =
+                   me8200_regbase + ME8200_DI_EXTEND_CHANGE_1_HIGH_REG;
+       } else {
+               PERROR("Wrong subdevice idx=%d.\n", di_idx);
+               kfree(subdevice);
+               return NULL;
+       }
+       subdevice->irq_ctrl_reg = me8200_regbase + ME8200_DI_IRQ_CTRL_REG;
+       subdevice->irq_mode_reg = me8200_regbase + ME8200_IRQ_MODE_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = me8200_regbase;
+#endif
+
+       /* Initialize wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_irq_start = me8200_di_io_irq_start;
+       subdevice->base.me_subdevice_io_irq_wait = me8200_di_io_irq_wait;
+       subdevice->base.me_subdevice_io_irq_stop = me8200_di_io_irq_stop;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8200_di_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me8200_di_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8200_di_io_single_read;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8200_di_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8200_di_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8200_di_query_subdevice_caps;
+       subdevice->base.me_subdevice_destructor = me8200_di_destructor;
+
+       subdevice->rised = 0;
+       subdevice->count = 0;
+
+       /* Register interrupt service routine. */
+       subdevice->irq = irq;
+       if (subdevice->version > 0) {   // NEW
+               err = request_irq(subdevice->irq, me8200_isr_EX,
+#ifdef IRQF_DISABLED
+                                 IRQF_DISABLED | IRQF_SHARED,
+#else
+                                 SA_INTERRUPT | SA_SHIRQ,
+#endif
+                                 ME8200_NAME, (void *)subdevice);
+       } else {                //OLD
+               err = request_irq(subdevice->irq, me8200_isr,
+#ifdef IRQF_DISABLED
+                                 IRQF_DISABLED | IRQF_SHARED,
+#else
+                                 SA_INTERRUPT | SA_SHIRQ,
+#endif
+                                 ME8200_NAME, (void *)subdevice);
+       }
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       PDEBUG("Registred irq=%d.\n", subdevice->irq);
+
+       return subdevice;
+}
+
+static void me8200_di_check_version(me8200_di_subdevice_t * instance,
+                                   unsigned long addr)
+{
+
+       PDEBUG("executed.\n");
+       instance->version = 0x000000FF & inb(addr);
+       PDEVELOP("me8200 firmware version: %d\n", instance->version);
+
+       /// @note Fix for wrong values in this registry.
+       if ((instance->version < 0x7) || (instance->version > 0x1F))
+               instance->version = 0x0;
+}
diff --git a/drivers/staging/meilhaus/me8200_di.h b/drivers/staging/meilhaus/me8200_di.h
new file mode 100644 (file)
index 0000000..2a3b005
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * @file me8200_di.h
+ *
+ * @brief ME-8200 digital input subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DI_H_
+#define _ME8200_DI_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me8200_di_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;
+       spinlock_t *irq_ctrl_lock;
+       spinlock_t *irq_mode_lock;
+
+       unsigned int di_idx;
+       unsigned int version;
+
+       int irq;                                                /**< The number of the interrupt request. */
+       volatile int rised;                             /**< Flag to indicate if an interrupt occured. */
+       uint status_flag;                               /**< Default interupt status flag */
+       uint status_value;                              /**< Interupt status */
+       uint status_value_edges;                        /**< Extended interupt status */
+       uint line_value;
+       int count;                                              /**< Counts the number of interrupts occured. */
+       uint8_t compare_value;
+       uint8_t filtering_flag;
+
+       wait_queue_head_t wait_queue;   /**< To wait on interrupts. */
+
+       unsigned long port_reg;                 /**< The digital input port. */
+       unsigned long compare_reg;              /**< The register to hold the value to compare with. */
+       unsigned long mask_reg;                 /**< The register to hold the mask. */
+       unsigned long irq_mode_reg;             /**< The interrupt mode register. */
+       unsigned long irq_ctrl_reg;             /**< The interrupt control register. */
+       unsigned long irq_status_reg;   /**< The interrupt status register. Also interrupt reseting register (firmware version 7 and later).*/
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+       unsigned long firmware_version_reg;     /**< The interrupt reseting register. */
+
+       unsigned long irq_status_low_reg;       /**< The interrupt extended status register (low part). */
+       unsigned long irq_status_high_reg;      /**< The interrupt extended status register (high part). */
+} me8200_di_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-8200 digital input subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8200_di_subdevice_t *me8200_di_constructor(uint32_t me8200_reg_base,
+                                            unsigned int di_idx,
+                                            int irq,
+                                            spinlock_t * irq_ctrl_lock,
+                                            spinlock_t * irq_mode_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_di_reg.h b/drivers/staging/meilhaus/me8200_di_reg.h
new file mode 100644 (file)
index 0000000..b9a619d
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * @file me8200_di_reg.h
+ *
+ * @brief ME-8200 digital input subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DI_REG_H_
+#define _ME8200_DI_REG_H_
+
+#ifdef __KERNEL__
+
+// Common registry for whole family.
+#define ME8200_DI_PORT_0_REG                                   0x3     // R
+#define ME8200_DI_PORT_1_REG                                   0x4     // R
+
+#define ME8200_DI_MASK_0_REG                                   0x5     // R/W
+#define ME8200_DI_MASK_1_REG                                   0x6     // R/W
+
+#define ME8200_DI_COMPARE_0_REG                                        0xA     // R/W
+#define ME8200_DI_COMPARE_1_REG                                        0xB     // R/W
+
+#define ME8200_DI_IRQ_CTRL_REG                                 0xC     // R/W
+
+#ifndef ME8200_IRQ_MODE_REG
+# define ME8200_IRQ_MODE_REG                                   0xD     // R/W
+#endif
+
+// This registry are for all versions
+#define ME8200_DI_CHANGE_0_REG                                 0xE     // R
+#define ME8200_DI_CHANGE_1_REG                                 0xF     // R
+
+#define ME8200_DI_IRQ_CTRL_BIT_CLEAR                   0x4
+#define ME8200_DI_IRQ_CTRL_BIT_ENABLE                  0x8
+
+// This registry are for firmware versions 7 and later
+#define ME8200_DI_EXTEND_CHANGE_0_LOW_REG              0x10    // R
+#define ME8200_DI_EXTEND_CHANGE_0_HIGH_REG             0x11    // R
+#define ME8200_DI_EXTEND_CHANGE_1_LOW_REG              0x12    // R
+#define ME8200_DI_EXTEND_CHANGE_1_HIGH_REG             0x13    // R
+
+#ifndef ME8200_FIRMWARE_VERSION_REG
+# define ME8200_FIRMWARE_VERSION_REG                   0x14    // R
+#endif
+
+// Bit definitions
+#define ME8200_DI_IRQ_CTRL_MASK_EDGE                   0x3
+#define ME8200_DI_IRQ_CTRL_MASK_EDGE_RISING            0x0
+#define ME8200_DI_IRQ_CTRL_MASK_EDGE_FALLING   0x1
+#define ME8200_DI_IRQ_CTRL_MASK_EDGE_ANY               0x3
+
+// Others
+#define ME8200_DI_IRQ_CTRL_SHIFT                               4
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_dio.c b/drivers/staging/meilhaus/me8200_dio.c
new file mode 100644 (file)
index 0000000..ff8ca1b
--- /dev/null
@@ -0,0 +1,418 @@
+/**
+ * @file me8200_dio.c
+ *
+ * @brief ME-8200 digital input/output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me8200_dio_reg.h"
+#include "me8200_dio.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me8200_dio_io_reset_subdevice(struct me_subdevice *subdevice,
+                                        struct file *filep, int flags)
+{
+       me8200_dio_subdevice_t *instance;
+       uint8_t mode;
+
+       PDEBUG("executed.\n");
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       instance = (me8200_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inb(instance->ctrl_reg);
+       mode &= ~(0x3 << (instance->dio_idx * 2));
+       outb(mode, instance->ctrl_reg);
+       PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->ctrl_reg - instance->reg_base, mode);
+       spin_unlock(instance->ctrl_reg_lock);
+       outb(0x00, instance->port_reg);
+       PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0x00);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_dio_io_single_config(me_subdevice_t * subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int single_config,
+                                      int ref,
+                                      int trig_chan,
+                                      int trig_type, int trig_edge, int flags)
+{
+       me8200_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint32_t mode;
+       uint32_t size =
+           flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
+                    | ME_IO_SINGLE_CONFIG_DIO_WORD |
+                    ME_IO_SINGLE_CONFIG_DIO_DWORD);
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       mode = inb(instance->ctrl_reg);
+       switch (size) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+                               mode &=
+                                   ~((ME8200_DIO_CTRL_BIT_MODE_0 |
+                                      ME8200_DIO_CTRL_BIT_MODE_1) <<
+                                     (instance->dio_idx * 2));
+                       } else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                               mode &=
+                                   ~((ME8200_DIO_CTRL_BIT_MODE_0 |
+                                      ME8200_DIO_CTRL_BIT_MODE_1) <<
+                                     (instance->dio_idx * 2));
+                               mode |=
+                                   ME8200_DIO_CTRL_BIT_MODE_0 << (instance->
+                                                                  dio_idx * 2);
+                       } else {
+                               PERROR
+                                   ("Invalid port configuration specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid channel number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid flags.\n");
+
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (!err) {
+               outb(mode, instance->ctrl_reg);
+               PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
+                          instance->reg_base,
+                          instance->ctrl_reg - instance->reg_base, mode);
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_dio_io_single_read(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int *value, int time_out, int flags)
+{
+       me8200_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
+                                             ME8200_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if ((mode ==
+                            (ME8200_DIO_CTRL_BIT_MODE_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value =
+                                   inb(instance->
+                                       port_reg) & (0x0001 << channel);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
+                                             ME8200_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if ((mode ==
+                            (ME8200_DIO_CTRL_BIT_MODE_0 <<
+                             (instance->dio_idx * 2))) || !mode) {
+                               *value = inb(instance->port_reg) & 0x00FF;
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_dio_io_single_write(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int value, int time_out, int flags)
+{
+       me8200_dio_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t mode;
+       uint8_t byte;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_dio_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
+                                             ME8200_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME8200_DIO_CTRL_BIT_MODE_0 <<
+                            (instance->dio_idx * 2))) {
+                               byte = inb(instance->port_reg);
+
+                               if (value)
+                                       byte |= 0x1 << channel;
+                               else
+                                       byte &= ~(0x1 << channel);
+
+                               outb(byte, instance->port_reg);
+                               PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->port_reg -
+                                          instance->reg_base, byte);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       mode =
+                           inb(instance->
+                               ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
+                                             ME8200_DIO_CTRL_BIT_MODE_1) <<
+                                            (instance->dio_idx * 2));
+
+                       if (mode ==
+                           (ME8200_DIO_CTRL_BIT_MODE_0 <<
+                            (instance->dio_idx * 2))) {
+                               outb(value, instance->port_reg);
+                               PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
+                                          instance->reg_base,
+                                          instance->port_reg -
+                                          instance->reg_base, value);
+                       } else {
+                               PERROR("Port not in output or input mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(instance->ctrl_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_dio_query_number_channels(me_subdevice_t * subdevice,
+                                           int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_dio_query_subdevice_type(me_subdevice_t * subdevice,
+                                          int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DIO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_dio_query_subdevice_caps(me_subdevice_t * subdevice,
+                                          int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_DIR_BYTE;
+       return ME_ERRNO_SUCCESS;
+}
+
+me8200_dio_subdevice_t *me8200_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock)
+{
+       me8200_dio_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me8200_dio_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8200_dio_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save digital i/o index */
+       subdevice->dio_idx = dio_idx;
+
+       /* Save the subdevice index */
+       subdevice->ctrl_reg = reg_base + ME8200_DIO_CTRL_REG;
+       subdevice->port_reg = reg_base + ME8200_DIO_PORT_REG + dio_idx;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8200_dio_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me8200_dio_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8200_dio_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me8200_dio_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8200_dio_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8200_dio_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8200_dio_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me8200_dio.h b/drivers/staging/meilhaus/me8200_dio.h
new file mode 100644 (file)
index 0000000..9ddd93d
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+ * @file me8200_dio.h
+ *
+ * @brief ME-8200 digital input/output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DIO_H_
+#define _ME8200_DIO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me8200_dio_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+       unsigned int dio_idx;                   /**< The index of the digital i/o on the device. */
+
+       unsigned long port_reg;                 /**< Register holding the port status. */
+       unsigned long ctrl_reg;                 /**< Register to configure the port direction. */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me8200_dio_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-8200 digital input/ouput subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param dio_idx The index of the digital i/o port on the device.
+ * @param ctrl_reg_lock Spin lock protecting the control register.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8200_dio_subdevice_t *me8200_dio_constructor(uint32_t reg_base,
+                                              unsigned int dio_idx,
+                                              spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_dio_reg.h b/drivers/staging/meilhaus/me8200_dio_reg.h
new file mode 100644 (file)
index 0000000..ac94a13
--- /dev/null
@@ -0,0 +1,43 @@
+/**
+ * @file me8200_dio_reg.h
+ *
+ * @brief ME-8200 digital input/output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DIO_REG_H_
+#define _ME8200_DIO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8200_DIO_CTRL_REG                                    0x7     // R/W
+#define ME8200_DIO_PORT_0_REG                          0x8     // R/W
+#define ME8200_DIO_PORT_1_REG                          0x9     // R/W
+#define ME8200_DIO_PORT_REG                                    ME8200_DIO_PORT_0_REG   // R/W
+
+#define ME8200_DIO_CTRL_BIT_MODE_0                     0x01
+#define ME8200_DIO_CTRL_BIT_MODE_1                     0x02
+#define ME8200_DIO_CTRL_BIT_MODE_2                     0x04
+#define ME8200_DIO_CTRL_BIT_MODE_3                     0x08
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_do.c b/drivers/staging/meilhaus/me8200_do.c
new file mode 100644 (file)
index 0000000..5f4ba5d
--- /dev/null
@@ -0,0 +1,600 @@
+/**
+ * @file me8200_do.c
+ *
+ * @brief ME-8200 digital output subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "meids.h"
+#include "medebug.h"
+#include "me8200_reg.h"
+#include "me8200_do_reg.h"
+#include "me8200_do.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static int me8200_do_io_irq_start(me_subdevice_t * subdevice,
+                                 struct file *filep,
+                                 int channel,
+                                 int irq_source,
+                                 int irq_edge, int irq_arg, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t tmp;
+       unsigned long status;
+
+       if (flags & ~ME_IO_IRQ_START_DIO_BYTE) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel != 0) {
+               PERROR("Invalid channel specified.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       if (irq_source != ME_IRQ_SOURCE_DIO_OVER_TEMP) {
+               PERROR("Invalid interrupt source specified.\n");
+               return ME_ERRNO_INVALID_IRQ_SOURCE;
+       }
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+       spin_lock(instance->irq_mode_lock);
+       tmp = inb(instance->irq_ctrl_reg);
+       tmp |=
+           ME8200_IRQ_MODE_BIT_ENABLE_POWER << (ME8200_IRQ_MODE_POWER_SHIFT *
+                                                instance->do_idx);
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->irq_mode_lock);
+       instance->rised = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_do_io_irq_wait(me_subdevice_t * subdevice,
+                                struct file *filep,
+                                int channel,
+                                int *irq_count,
+                                int *value, int time_out, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       long t = 0;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (time_out < 0) {
+               PERROR("Invalid time_out specified.\n");
+               return ME_ERRNO_INVALID_TIMEOUT;
+       }
+
+       if (time_out) {
+               t = (time_out * HZ) / 1000;
+
+               if (t == 0)
+                       t = 1;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       if (instance->rised <= 0) {
+               instance->rised = 0;
+
+               if (time_out) {
+                       t = wait_event_interruptible_timeout(instance->
+                                                            wait_queue,
+                                                            (instance->rised !=
+                                                             0), t);
+
+                       if (t == 0) {
+                               PERROR
+                                   ("Wait on external interrupt timed out.\n");
+                               err = ME_ERRNO_TIMEOUT;
+                       }
+               } else {
+                       wait_event_interruptible(instance->wait_queue,
+                                                (instance->rised != 0));
+               }
+
+               if (instance->rised < 0) {
+                       PERROR("Wait on interrupt aborted by user.\n");
+                       err = ME_ERRNO_CANCELLED;
+               }
+       }
+
+       if (signal_pending(current)) {
+               PERROR("Wait on external interrupt aborted by signal.\n");
+               err = ME_ERRNO_SIGNAL;
+       }
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       instance->rised = 0;
+       *irq_count = instance->count;
+       *value = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_do_io_irq_stop(me_subdevice_t * subdevice,
+                                struct file *filep, int channel, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       uint8_t tmp;
+       unsigned long cpu_flags;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       spin_lock(instance->irq_mode_lock);
+       tmp = inb(instance->irq_ctrl_reg);
+       tmp &=
+           ~(ME8200_IRQ_MODE_BIT_ENABLE_POWER <<
+             (ME8200_IRQ_MODE_POWER_SHIFT * instance->do_idx));
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->irq_mode_lock);
+       instance->rised = -1;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_do_io_reset_subdevice(struct me_subdevice *subdevice,
+                                       struct file *filep, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       unsigned long cpu_flags;
+       uint8_t tmp;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
+       outb(0x00, instance->port_reg);
+       PDEBUG_REG("port_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->port_reg - instance->reg_base, 0x00);
+       spin_lock(instance->irq_mode_lock);
+       tmp = inb(instance->irq_ctrl_reg);
+       tmp &=
+           ~(ME8200_IRQ_MODE_BIT_ENABLE_POWER <<
+             (ME8200_IRQ_MODE_POWER_SHIFT * instance->do_idx));
+       outb(tmp, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, tmp);
+       spin_unlock(instance->irq_mode_lock);
+       instance->rised = -1;
+       instance->count = 0;
+       spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_do_io_single_config(me_subdevice_t * subdevice,
+                                     struct file *filep,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+       switch (flags) {
+       case ME_IO_SINGLE_CONFIG_NO_FLAGS:
+       case ME_IO_SINGLE_CONFIG_DIO_BYTE:
+               if (channel == 0) {
+                       if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+                       } else {
+                               PERROR("Invalid byte direction specified.\n");
+                               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_do_io_single_read(me_subdevice_t * subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inb(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_do_io_single_write(me_subdevice_t * subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       me8200_do_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+       uint8_t state;
+       unsigned long status;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock_irqsave(&instance->subdevice_lock, status);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       state = inb(instance->port_reg);
+                       state =
+                           value ? (state | (0x1 << channel)) : (state &
+                                                                 ~(0x1 <<
+                                                                   channel));
+                       outb(state, instance->port_reg);
+                       PDEBUG_REG("port_reg outb(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->port_reg - instance->reg_base,
+                                  state);
+               } else {
+                       PERROR("Invalid bit number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       outb(value, instance->port_reg);
+                       PDEBUG_REG("port_reg outb(0x%lX+0x%lX)=0x%x\n",
+                                  instance->reg_base,
+                                  instance->port_reg - instance->reg_base,
+                                  value);
+               } else {
+                       PERROR("Invalid byte number specified.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock_irqrestore(&instance->subdevice_lock, status);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8200_do_query_number_channels(me_subdevice_t * subdevice,
+                                          int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 8;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_do_query_subdevice_type(me_subdevice_t * subdevice,
+                                         int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8200_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_OVER_TEMP_IRQ;
+       return ME_ERRNO_SUCCESS;
+}
+
+static void me8200_do_destructor(struct me_subdevice *subdevice)
+{
+       me8200_do_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8200_do_subdevice_t *) subdevice;
+
+       free_irq(instance->irq, (void *)instance);
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static irqreturn_t me8200_do_isr(int irq, void *dev_id)
+#else
+static irqreturn_t me8200_do_isr(int irq, void *dev_id, struct pt_regs *regs)
+#endif
+{
+       me8200_do_subdevice_t *instance;
+       uint16_t ctrl;
+       uint8_t irq_status;
+
+       instance = (me8200_do_subdevice_t *) dev_id;
+
+       if (irq != instance->irq) {
+               PERROR("Incorrect interrupt num: %d.\n", irq);
+               return IRQ_NONE;
+       }
+
+       irq_status = inb(instance->irq_status_reg);
+       if (!
+           (irq_status &
+            (ME8200_DO_IRQ_STATUS_BIT_ACTIVE << instance->do_idx))) {
+               PINFO
+                   ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n",
+                    jiffies, __FUNCTION__, instance->do_idx, irq_status);
+               return IRQ_NONE;
+       }
+
+       PDEBUG("executed.\n");
+
+       spin_lock(&instance->subdevice_lock);
+       instance->rised = 1;
+       instance->count++;
+
+       spin_lock(instance->irq_mode_lock);
+       ctrl = inw(instance->irq_ctrl_reg);
+       ctrl |= ME8200_IRQ_MODE_BIT_CLEAR_POWER << instance->do_idx;
+       outw(ctrl, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, ctrl);
+       ctrl &= ~(ME8200_IRQ_MODE_BIT_CLEAR_POWER << instance->do_idx);
+       outw(ctrl, instance->irq_ctrl_reg);
+       PDEBUG_REG("irq_ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
+                  instance->irq_ctrl_reg - instance->reg_base, ctrl);
+       spin_unlock(instance->irq_mode_lock);
+       spin_unlock(&instance->subdevice_lock);
+       wake_up_interruptible_all(&instance->wait_queue);
+
+       return IRQ_HANDLED;
+}
+
+me8200_do_subdevice_t *me8200_do_constructor(uint32_t reg_base,
+                                            unsigned int do_idx,
+                                            int irq,
+                                            spinlock_t * irq_mode_lock)
+{
+       me8200_do_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me8200_do_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8200_do_subdevice_t));
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->irq_mode_lock = irq_mode_lock;
+
+       /* Save the index of the digital output */
+       subdevice->do_idx = do_idx;
+       subdevice->irq = irq;
+
+       /* Initialize the registers */
+       if (do_idx == 0) {
+               subdevice->port_reg = reg_base + ME8200_DO_PORT_0_REG;
+       } else if (do_idx == 1) {
+               subdevice->port_reg = reg_base + ME8200_DO_PORT_1_REG;
+       } else {
+               PERROR("Wrong subdevice idx=%d.\n", do_idx);
+               kfree(subdevice);
+               return NULL;
+       }
+       subdevice->irq_ctrl_reg = reg_base + ME8200_IRQ_MODE_REG;
+       subdevice->irq_status_reg = reg_base + ME8200_DO_IRQ_STATUS_REG;
+#ifdef MEDEBUG_DEBUG_REG
+       subdevice->reg_base = reg_base;
+#endif
+
+       /* Initialize the wait queue */
+       init_waitqueue_head(&subdevice->wait_queue);
+
+       /* Request the interrupt line */
+       err = request_irq(irq, me8200_do_isr,
+#ifdef IRQF_DISABLED
+                         IRQF_DISABLED | IRQF_SHARED,
+#else
+                         SA_INTERRUPT | SA_SHIRQ,
+#endif
+                         ME8200_NAME, (void *)subdevice);
+
+       if (err) {
+               PERROR("Cannot get interrupt line.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       PINFO("Registered irq=%d.\n", irq);
+
+       /* Overload base class methods. */
+       subdevice->base.me_subdevice_io_irq_start = me8200_do_io_irq_start;
+       subdevice->base.me_subdevice_io_irq_wait = me8200_do_io_irq_wait;
+       subdevice->base.me_subdevice_io_irq_stop = me8200_do_io_irq_stop;
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8200_do_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config =
+           me8200_do_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8200_do_io_single_read;
+       subdevice->base.me_subdevice_io_single_write =
+           me8200_do_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8200_do_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8200_do_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8200_do_query_subdevice_caps;
+       subdevice->base.me_subdevice_destructor = me8200_do_destructor;
+
+       subdevice->rised = 0;
+       subdevice->count = 0;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me8200_do.h b/drivers/staging/meilhaus/me8200_do.h
new file mode 100644 (file)
index 0000000..2758125
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * @file me8200_do.h
+ *
+ * @brief ME-8200 digital output subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DO_H_
+#define _ME8200_DO_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The template subdevice class.
+ */
+typedef struct me8200_do_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *irq_mode_lock;
+
+       int irq;                                /**< The number of the interrupt request */
+       int rised;                              /**< Flag to indicate if an interrupt occured */
+       int count;                              /**< Counts the number of interrupts occured */
+       wait_queue_head_t wait_queue;           /**< To wait on interrupts */
+
+       unsigned int do_idx;                    /**< The number of the digital output */
+
+       unsigned long port_reg;                 /**< The digital output port */
+       unsigned long irq_ctrl_reg;             /**< The interrupt control register */
+       unsigned long irq_status_reg;           /**< The interrupt status register */
+#ifdef MEDEBUG_DEBUG_REG
+       unsigned long reg_base;
+#endif
+} me8200_do_subdevice_t;
+
+/**
+ * @brief The constructor to generate a ME-8200 digital output subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param do_idx The index of the digital output subdevice on this device.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8200_do_subdevice_t *me8200_do_constructor(uint32_t reg_base,
+                                            unsigned int do_idx,
+                                            int irq,
+                                            spinlock_t * irq_mode_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_do_reg.h b/drivers/staging/meilhaus/me8200_do_reg.h
new file mode 100644 (file)
index 0000000..4109504
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+ * @file me8200_ao_reg.h
+ *
+ * @brief ME-8200 analog output subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_DO_REG_H_
+#define _ME8200_DO_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8200_DO_IRQ_STATUS_REG                       0x0     // R
+#define ME8200_DO_PORT_0_REG                           0x1     // R/W
+#define ME8200_DO_PORT_1_REG                           0x2     // R/W
+
+#define ME8200_DO_IRQ_STATUS_BIT_ACTIVE                0x1
+#define ME8200_DO_IRQ_STATUS_SHIFT                     1
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8200_reg.h b/drivers/staging/meilhaus/me8200_reg.h
new file mode 100644 (file)
index 0000000..a73fe4d
--- /dev/null
@@ -0,0 +1,46 @@
+/**
+ * @file me8200_reg.h
+ *
+ * @brief ME-8200 register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8200_REG_H_
+#define _ME8200_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8200_IRQ_MODE_REG                            0xD     // R/W
+
+#define ME8200_IRQ_MODE_MASK                           0x3
+
+#define ME8200_IRQ_MODE_MASK_MASK                      0x0
+#define ME8200_IRQ_MODE_MASK_COMPARE                   0x1
+
+#define ME8200_IRQ_MODE_BIT_ENABLE_POWER               0x10
+#define ME8200_IRQ_MODE_BIT_CLEAR_POWER                        0x40
+
+#define ME8200_IRQ_MODE_DI_SHIFT                       2
+#define ME8200_IRQ_MODE_POWER_SHIFT                    1
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8254.c b/drivers/staging/meilhaus/me8254.c
new file mode 100644 (file)
index 0000000..6e44c3d
--- /dev/null
@@ -0,0 +1,1176 @@
+/**
+ * @file me8254.c
+ *
+ * @brief 8254 subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "me8254_reg.h"
+#include "me8254.h"
+
+/*
+ * Defines
+ */
+#define ME8254_NUMBER_CHANNELS 1       /**< One channel per counter. */
+#define ME8254_CTR_WIDTH 16                    /**< One counter has 16 bits. */
+
+/*
+ * Functions
+ */
+
+static int me8254_io_reset_subdevice(struct me_subdevice *subdevice,
+                                    struct file *filep, int flags)
+{
+       me8254_subdevice_t *instance;
+       uint8_t clk_src;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8254_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       if (instance->ctr_idx == 0)
+               outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M0 |
+                    ME8254_CTRL_BIN, instance->ctrl_reg);
+       else if (instance->ctr_idx == 1)
+               outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M0 |
+                    ME8254_CTRL_BIN, instance->ctrl_reg);
+       else
+               outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M0 |
+                    ME8254_CTRL_BIN, instance->ctrl_reg);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outb(0x00, instance->val_reg);
+       outb(0x00, instance->val_reg);
+
+       spin_lock(instance->clk_src_reg_lock);
+       clk_src = inb(instance->clk_src_reg);
+
+       switch (instance->device_id) {
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               if (instance->me8254_idx == 0) {
+                       if (instance->ctr_idx == 0)
+                               clk_src &=
+                                   ~(ME1400AB_8254_A_0_CLK_SRC_10MHZ |
+                                     ME1400AB_8254_A_0_CLK_SRC_QUARZ);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400AB_8254_A_1_CLK_SRC_PREV);
+                       else
+                               clk_src &= ~(ME1400AB_8254_A_2_CLK_SRC_PREV);
+               } else {
+                       if (instance->ctr_idx == 0)
+                               clk_src &=
+                                   ~(ME1400AB_8254_B_0_CLK_SRC_10MHZ |
+                                     ME1400AB_8254_B_0_CLK_SRC_QUARZ);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400AB_8254_B_1_CLK_SRC_PREV);
+                       else
+                               clk_src &= ~(ME1400AB_8254_B_2_CLK_SRC_PREV);
+               }
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               switch (instance->me8254_idx) {
+               case 0:
+               case 2:
+               case 4:
+               case 6:
+               case 8:
+                       if (instance->ctr_idx == 0)
+                               clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400CD_8254_ACE_1_CLK_SRC_MASK);
+                       else
+                               clk_src &= ~(ME1400CD_8254_ACE_2_CLK_SRC_MASK);
+                       break;
+
+               default:
+                       if (instance->ctr_idx == 0)
+                               clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400CD_8254_BD_1_CLK_SRC_MASK);
+                       else
+                               clk_src &= ~(ME1400CD_8254_BD_2_CLK_SRC_MASK);
+                       break;
+               }
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4610:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_A:
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_B:
+
+               /* No clock source register available */
+               break;
+
+       default:
+               PERROR("Invalid device type.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       if (!err)
+               outb(clk_src, instance->clk_src_reg);
+
+       spin_unlock(instance->clk_src_reg_lock);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me1400_ab_ref_config(me8254_subdevice_t * instance, int ref)
+{
+       uint8_t clk_src;
+
+       spin_lock(instance->clk_src_reg_lock);
+       clk_src = inb(instance->clk_src_reg);
+
+       switch (ref) {
+       case ME_REF_CTR_EXTERNAL:
+               if (instance->me8254_idx == 0) {
+                       if (instance->ctr_idx == 0)
+                               clk_src &= ~(ME1400AB_8254_A_0_CLK_SRC_QUARZ);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400AB_8254_A_1_CLK_SRC_PREV);
+                       else
+                               clk_src &= ~(ME1400AB_8254_A_2_CLK_SRC_PREV);
+               } else {
+                       if (instance->ctr_idx == 0)
+                               clk_src &= ~(ME1400AB_8254_B_0_CLK_SRC_QUARZ);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400AB_8254_B_1_CLK_SRC_PREV);
+                       else
+                               clk_src &= ~(ME1400AB_8254_B_2_CLK_SRC_PREV);
+               }
+
+               break;
+
+       case ME_REF_CTR_PREVIOUS:
+               if (instance->me8254_idx == 0) {
+                       if (instance->ctr_idx == 0) {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       } else if (instance->ctr_idx == 1)
+                               clk_src |= (ME1400AB_8254_A_1_CLK_SRC_PREV);
+                       else
+                               clk_src |= (ME1400AB_8254_A_2_CLK_SRC_PREV);
+               } else {
+                       if (instance->ctr_idx == 0) {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       } else if (instance->ctr_idx == 1)
+                               clk_src |= (ME1400AB_8254_B_1_CLK_SRC_PREV);
+                       else
+                               clk_src |= (ME1400AB_8254_B_2_CLK_SRC_PREV);
+               }
+
+               break;
+
+       case ME_REF_CTR_INTERNAL_1MHZ:
+               if (instance->me8254_idx == 0) {
+                       if (instance->ctr_idx == 0) {
+                               clk_src |= (ME1400AB_8254_A_0_CLK_SRC_QUARZ);
+                               clk_src &= ~(ME1400AB_8254_A_0_CLK_SRC_10MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       if (instance->ctr_idx == 0) {
+                               clk_src |= (ME1400AB_8254_B_0_CLK_SRC_QUARZ);
+                               clk_src &= ~(ME1400AB_8254_B_0_CLK_SRC_10MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               }
+
+               break;
+
+       case ME_REF_CTR_INTERNAL_10MHZ:
+               if (instance->me8254_idx == 0) {
+                       if (instance->ctr_idx == 0) {
+                               clk_src |= (ME1400AB_8254_A_0_CLK_SRC_QUARZ);
+                               clk_src |= (ME1400AB_8254_A_0_CLK_SRC_10MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               } else {
+                       if (instance->ctr_idx == 0) {
+                               clk_src |= (ME1400AB_8254_A_0_CLK_SRC_QUARZ);
+                               clk_src |= (ME1400AB_8254_A_0_CLK_SRC_10MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+                       }
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid reference.\n");
+               spin_unlock(instance->clk_src_reg_lock);
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       outb(clk_src, instance->clk_src_reg);
+       spin_unlock(instance->clk_src_reg_lock);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me1400_cd_ref_config(me8254_subdevice_t * instance, int ref)
+{
+       uint8_t clk_src;
+
+       spin_lock(instance->clk_src_reg_lock);
+       clk_src = inb(instance->clk_src_reg);
+
+       switch (ref) {
+       case ME_REF_CTR_EXTERNAL:
+               switch (instance->me8254_idx) {
+               case 0:
+               case 2:
+               case 4:
+               case 6:
+               case 8:
+                       if (instance->ctr_idx == 0)
+                               clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400CD_8254_ACE_1_CLK_SRC_MASK);
+                       else
+                               clk_src &= ~(ME1400CD_8254_ACE_2_CLK_SRC_MASK);
+                       break;
+
+               default:
+                       if (instance->ctr_idx == 0)
+                               clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK);
+                       else if (instance->ctr_idx == 1)
+                               clk_src &= ~(ME1400CD_8254_BD_1_CLK_SRC_MASK);
+                       else
+                               clk_src &= ~(ME1400CD_8254_BD_2_CLK_SRC_MASK);
+                       break;
+               }
+               break;
+
+       case ME_REF_CTR_PREVIOUS:
+               switch (instance->me8254_idx) {
+               case 0:
+               case 2:
+               case 4:
+               case 6:
+               case 8:
+                       if (instance->ctr_idx == 0) {
+                               clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_ACE_0_CLK_SRC_PREV);
+                       } else if (instance->ctr_idx == 1) {
+                               clk_src &= ~(ME1400CD_8254_ACE_1_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_ACE_1_CLK_SRC_PREV);
+                       } else {
+                               clk_src &= ~(ME1400CD_8254_ACE_2_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_ACE_2_CLK_SRC_PREV);
+                       }
+                       break;
+
+               default:
+                       if (instance->ctr_idx == 0) {
+                               clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_BD_0_CLK_SRC_PREV);
+                       } else if (instance->ctr_idx == 1) {
+                               clk_src &= ~(ME1400CD_8254_BD_1_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_BD_1_CLK_SRC_PREV);
+                       } else {
+                               clk_src &= ~(ME1400CD_8254_BD_2_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_BD_2_CLK_SRC_PREV);
+                       }
+                       break;
+               }
+
+               break;
+
+       case ME_REF_CTR_INTERNAL_1MHZ:
+               switch (instance->me8254_idx) {
+               case 0:
+               case 2:
+               case 4:
+               case 6:
+               case 8:
+                       if (instance->ctr_idx == 0) {
+                               clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_ACE_0_CLK_SRC_1MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_REF;
+                       }
+
+                       break;
+
+               default:
+                       if (instance->ctr_idx == 0) {
+                               clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_BD_0_CLK_SRC_1MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_REF;
+                       }
+                       break;
+               }
+
+               break;
+
+       case ME_REF_CTR_INTERNAL_10MHZ:
+               switch (instance->me8254_idx) {
+               case 0:
+               case 2:
+               case 4:
+               case 6:
+               case 8:
+                       if (instance->ctr_idx == 0) {
+                               clk_src &= ~(ME1400CD_8254_ACE_0_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_ACE_0_CLK_SRC_10MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_REF;
+                       }
+                       break;
+
+               default:
+                       if (instance->ctr_idx == 0) {
+                               clk_src &= ~(ME1400CD_8254_BD_0_CLK_SRC_MASK);
+                               clk_src |= (ME1400CD_8254_BD_0_CLK_SRC_10MHZ);
+                       } else {
+                               PERROR("Invalid reference.\n");
+                               spin_unlock(instance->clk_src_reg_lock);
+                               return ME_ERRNO_INVALID_REF;
+                       }
+
+                       break;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid reference.\n");
+               spin_unlock(instance->clk_src_reg_lock);
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       outb(clk_src, instance->clk_src_reg);
+       spin_unlock(instance->clk_src_reg_lock);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me4600_ref_config(me8254_subdevice_t * instance, int ref)
+{
+       switch (ref) {
+
+       case ME_REF_CTR_EXTERNAL:
+               // Nothing to do
+               break;
+
+       default:
+               PERROR("Invalid reference.\n");
+//                      spin_unlock(instance->clk_src_reg_lock);
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8100_ref_config(me8254_subdevice_t * instance, int ref)
+{
+       switch (ref) {
+
+       case ME_REF_CTR_EXTERNAL:
+               // Nothing to do
+               break;
+
+       default:
+               PERROR("Invalid reference.\n");
+//                      spin_unlock(instance->clk_src_reg_lock);
+               return ME_ERRNO_INVALID_REF;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_io_single_config(struct me_subdevice *subdevice,
+                                  struct file *filep,
+                                  int channel,
+                                  int single_config,
+                                  int ref,
+                                  int trig_chan,
+                                  int trig_type, int trig_edge, int flags)
+{
+       me8254_subdevice_t *instance;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       instance = (me8254_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       // Configure the counter modes
+       if (instance->ctr_idx == 0) {
+               if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_0) {
+                       outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M0 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_1) {
+                       outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M1 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_2) {
+                       outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M2 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_3) {
+                       outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M3 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_4) {
+                       outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M4 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_5) {
+                       outb(ME8254_CTRL_SC0 | ME8254_CTRL_LM | ME8254_CTRL_M5 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else {
+                       PERROR("Invalid single configuration.\n");
+                       spin_unlock(&instance->subdevice_lock);
+                       return ME_ERRNO_INVALID_SINGLE_CONFIG;
+               }
+       } else if (instance->ctr_idx == 1) {
+               if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_0) {
+                       outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M0 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_1) {
+                       outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M1 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_2) {
+                       outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M2 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_3) {
+                       outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M3 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_4) {
+                       outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M4 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_5) {
+                       outb(ME8254_CTRL_SC1 | ME8254_CTRL_LM | ME8254_CTRL_M5 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else {
+                       PERROR("Invalid single configuration.\n");
+                       spin_unlock(&instance->subdevice_lock);
+                       return ME_ERRNO_INVALID_SINGLE_CONFIG;
+               }
+       } else {
+               if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_0) {
+                       outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M0 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_1) {
+                       outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M1 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_2) {
+                       outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M2 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_3) {
+                       outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M3 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_4) {
+                       outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M4 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else if (single_config == ME_SINGLE_CONFIG_CTR_8254_MODE_5) {
+                       outb(ME8254_CTRL_SC2 | ME8254_CTRL_LM | ME8254_CTRL_M5 |
+                            ME8254_CTRL_BIN, instance->ctrl_reg);
+               } else {
+                       PERROR("Invalid single configuration.\n");
+                       spin_unlock(&instance->subdevice_lock);
+                       return ME_ERRNO_INVALID_SINGLE_CONFIG;
+               }
+       }
+
+       switch (instance->device_id) {
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               err = me1400_ab_ref_config(instance, ref);
+
+               if (err) {
+                       spin_unlock(&instance->subdevice_lock);
+                       return err;
+               }
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               err = me1400_cd_ref_config(instance, ref);
+
+               if (err) {
+                       spin_unlock(&instance->subdevice_lock);
+                       return err;
+               }
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4610:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680IS:
+               err = me4600_ref_config(instance, ref);
+
+               if (err) {
+                       spin_unlock(&instance->subdevice_lock);
+                       return err;
+               }
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_A:
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_B:
+               err = me8100_ref_config(instance, ref);
+
+               if (err) {
+                       spin_unlock(&instance->subdevice_lock);
+                       return err;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid device type.\n");
+
+               spin_unlock(&instance->subdevice_lock);
+//                              spin_unlock(instance->clk_src_reg_lock);
+               return ME_ERRNO_INVALID_SINGLE_CONFIG;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_io_single_read(struct me_subdevice *subdevice,
+                                struct file *filep,
+                                int channel,
+                                int *value, int time_out, int flags)
+{
+       me8254_subdevice_t *instance;
+       uint16_t lo_byte;
+       uint16_t hi_byte;
+
+       PDEBUG("executed.\n");
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       instance = (me8254_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       if (instance->ctr_idx == 0)
+               outb(ME8254_CTRL_SC0 | ME8254_CTRL_TLO, instance->ctrl_reg);
+       else if (instance->ctr_idx == 1)
+               outb(ME8254_CTRL_SC1 | ME8254_CTRL_TLO, instance->ctrl_reg);
+       else
+               outb(ME8254_CTRL_SC2 | ME8254_CTRL_TLO, instance->ctrl_reg);
+
+       lo_byte = inb(instance->val_reg);
+       hi_byte = inb(instance->val_reg);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       *value = lo_byte | (hi_byte << 8);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_io_single_write(struct me_subdevice *subdevice,
+                                 struct file *filep,
+                                 int channel,
+                                 int value, int time_out, int flags)
+{
+       me8254_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       instance = (me8254_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       outb(value, instance->val_reg);
+       outb((value >> 8), instance->val_reg);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_query_number_channels(struct me_subdevice *subdevice,
+                                       int *number)
+{
+       PDEBUG("executed.\n");
+       *number = ME8254_NUMBER_CHANNELS;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_query_subdevice_type(struct me_subdevice *subdevice,
+                                      int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_CTR;
+       *subtype = ME_SUBTYPE_CTR_8254;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_query_subdevice_caps(struct me_subdevice *subdevice,
+                                      int *caps)
+{
+       me8254_subdevice_t *instance;
+       PDEBUG("executed.\n");
+       instance = (me8254_subdevice_t *) subdevice;
+       *caps = instance->caps;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8254_query_subdevice_caps_args(struct me_subdevice *subdevice,
+                                           int cap, int *args, int count)
+{
+       PDEBUG("executed.\n");
+
+       if (count != 1) {
+               PERROR("Invalid capability argument count.\n");
+               return ME_ERRNO_INVALID_CAP_ARG_COUNT;
+       }
+
+       if (cap == ME_CAP_CTR_WIDTH) {
+               args[0] = ME8254_CTR_WIDTH;
+       } else {
+               PERROR("Invalid capability.\n");
+               return ME_ERRNO_INVALID_CAP;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static uint32_t me1400AB_get_val_reg(uint32_t reg_base, unsigned int me8254_idx,
+                                    unsigned int ctr_idx)
+{
+       switch (me8254_idx) {
+
+       case 0:
+               return (reg_base + ME1400AB_8254_A_0_VAL_REG + ctr_idx);
+
+       default:
+               return (reg_base + ME1400AB_8254_B_0_VAL_REG + ctr_idx);
+       }
+
+       return 0;
+}
+
+static uint32_t me1400AB_get_ctrl_reg(uint32_t reg_base,
+                                     unsigned int me8254_idx,
+                                     unsigned int ctr_idx)
+{
+       switch (me8254_idx) {
+       case 0:
+               return (reg_base + ME1400AB_8254_A_CTRL_REG);
+
+       default:
+               return (reg_base + ME1400AB_8254_B_CTRL_REG);
+       }
+
+       return 0;
+}
+
+static uint32_t me1400AB_get_clk_src_reg(uint32_t reg_base,
+                                        unsigned int me8254_idx,
+                                        unsigned int ctr_idx)
+{
+       switch (me8254_idx) {
+       case 0:
+               return (reg_base + ME1400AB_CLK_SRC_REG);
+
+       default:
+               return (reg_base + ME1400AB_CLK_SRC_REG);
+       }
+
+       return 0;
+}
+
+static uint32_t me1400CD_get_val_reg(uint32_t reg_base, unsigned int me8254_idx,
+                                    unsigned int ctr_idx)
+{
+       switch (me8254_idx) {
+       case 0:
+               return (reg_base + ME1400C_8254_A_0_VAL_REG + ctr_idx);
+
+       case 1:
+               return (reg_base + ME1400C_8254_B_0_VAL_REG + ctr_idx);
+
+       case 2:
+               return (reg_base + ME1400C_8254_C_0_VAL_REG + ctr_idx);
+
+       case 3:
+               return (reg_base + ME1400C_8254_D_0_VAL_REG + ctr_idx);
+
+       case 4:
+               return (reg_base + ME1400C_8254_E_0_VAL_REG + ctr_idx);
+
+       case 5:
+               return (reg_base + ME1400D_8254_A_0_VAL_REG + ctr_idx);
+
+       case 6:
+               return (reg_base + ME1400D_8254_B_0_VAL_REG + ctr_idx);
+
+       case 7:
+               return (reg_base + ME1400D_8254_C_0_VAL_REG + ctr_idx);
+
+       case 8:
+               return (reg_base + ME1400D_8254_D_0_VAL_REG + ctr_idx);
+
+       default:
+               return (reg_base + ME1400D_8254_E_0_VAL_REG + ctr_idx);
+       }
+
+       return 0;
+}
+
+static uint32_t me1400CD_get_ctrl_reg(uint32_t reg_base,
+                                     unsigned int me8254_idx,
+                                     unsigned int ctr_idx)
+{
+       switch (me8254_idx) {
+       case 0:
+               return (reg_base + ME1400C_8254_A_CTRL_REG);
+
+       case 1:
+               return (reg_base + ME1400C_8254_B_CTRL_REG);
+
+       case 2:
+               return (reg_base + ME1400C_8254_C_CTRL_REG);
+
+       case 3:
+               return (reg_base + ME1400C_8254_D_CTRL_REG);
+
+       case 4:
+               return (reg_base + ME1400C_8254_E_CTRL_REG);
+
+       case 5:
+               return (reg_base + ME1400D_8254_A_CTRL_REG);
+
+       case 6:
+               return (reg_base + ME1400D_8254_B_CTRL_REG);
+
+       case 7:
+               return (reg_base + ME1400D_8254_C_CTRL_REG);
+
+       case 8:
+               return (reg_base + ME1400D_8254_D_CTRL_REG);
+
+       default:
+               return (reg_base + ME1400D_8254_E_CTRL_REG);
+       }
+
+       return 0;
+}
+
+static uint32_t me1400CD_get_clk_src_reg(uint32_t reg_base,
+                                        unsigned int me8254_idx,
+                                        unsigned int ctr_idx)
+{
+       switch (me8254_idx) {
+       case 0:
+               return (reg_base + ME1400C_CLK_SRC_0_REG);
+
+       case 1:
+               return (reg_base + ME1400C_CLK_SRC_0_REG);
+
+       case 2:
+               return (reg_base + ME1400C_CLK_SRC_1_REG);
+
+       case 3:
+               return (reg_base + ME1400C_CLK_SRC_1_REG);
+
+       case 4:
+               return (reg_base + ME1400C_CLK_SRC_2_REG);
+
+       case 5:
+               return (reg_base + ME1400D_CLK_SRC_0_REG);
+
+       case 6:
+               return (reg_base + ME1400D_CLK_SRC_0_REG);
+
+       case 7:
+               return (reg_base + ME1400D_CLK_SRC_1_REG);
+
+       case 8:
+               return (reg_base + ME1400D_CLK_SRC_1_REG);
+
+       default:
+               return (reg_base + ME1400D_CLK_SRC_2_REG);
+       }
+
+       return 0;
+}
+
+static uint32_t me4600_get_val_reg(uint32_t reg_base, unsigned int me8254_idx,
+                                  unsigned int ctr_idx)
+{
+       return (reg_base + ME4600_8254_0_VAL_REG + ctr_idx);
+}
+
+static uint32_t me4600_get_ctrl_reg(uint32_t reg_base, unsigned int me8254_idx,
+                                   unsigned int ctr_idx)
+{
+       return (reg_base + ME4600_8254_CTRL_REG);
+}
+
+static uint32_t me8100_get_val_reg(uint32_t reg_base, unsigned int me8254_idx,
+                                  unsigned int ctr_idx)
+{
+       return (reg_base + ME8100_COUNTER_REG_0 + ctr_idx * 2);
+}
+
+static uint32_t me8100_get_ctrl_reg(uint32_t reg_base, unsigned int me8254_idx,
+                                   unsigned int ctr_idx)
+{
+       return (reg_base + ME8100_COUNTER_CTRL_REG);
+}
+
+me8254_subdevice_t *me8254_constructor(uint32_t device_id,
+                                      uint32_t reg_base,
+                                      unsigned int me8254_idx,
+                                      unsigned int ctr_idx,
+                                      spinlock_t * ctrl_reg_lock,
+                                      spinlock_t * clk_src_reg_lock)
+{
+       me8254_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       // Allocate memory for subdevice instance
+       subdevice = kmalloc(sizeof(me8254_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for 8254 instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8254_subdevice_t));
+
+       // Check if counter index is out of range
+
+       if (ctr_idx > 2) {
+               PERROR("Counter index is out of range.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize subdevice base class
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+       subdevice->clk_src_reg_lock = clk_src_reg_lock;
+
+       // Save type of Meilhaus device
+       subdevice->device_id = device_id;
+
+       // Save the indices
+       subdevice->me8254_idx = me8254_idx;
+       subdevice->ctr_idx = ctr_idx;
+
+       // Do device specific initialization
+       switch (device_id) {
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+               // Check if 8254 index is out of range
+               if (me8254_idx > 0) {
+                       PERROR("8254 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:     // Fall through
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               // Check if 8254 index is out of range
+               if (me8254_idx > 1) {
+                       PERROR("8254 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+               // Initialize the counters capabilities
+               if (ctr_idx == 0)
+                       subdevice->caps =
+                           ME_CAPS_CTR_CLK_INTERNAL_1MHZ |
+                           ME_CAPS_CTR_CLK_INTERNAL_10MHZ |
+                           ME_CAPS_CTR_CLK_EXTERNAL;
+               else
+                       subdevice->caps =
+                           ME_CAPS_CTR_CLK_PREVIOUS | ME_CAPS_CTR_CLK_EXTERNAL;
+
+               // Get the counters registers
+               subdevice->val_reg =
+                   me1400AB_get_val_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->ctrl_reg =
+                   me1400AB_get_ctrl_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->clk_src_reg =
+                   me1400AB_get_clk_src_reg(reg_base, me8254_idx, ctr_idx);
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+               // Check if 8254 index is out of range
+               if (me8254_idx > 4) {
+                       PERROR("8254 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               // Check if 8254 index is out of range
+               if (me8254_idx > 9) {
+                       PERROR("8254 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+               // Initialize the counters capabilities
+               if (ctr_idx == 0) {
+                       if (me8254_idx == 0)
+                               subdevice->caps =
+                                   ME_CAPS_CTR_CLK_PREVIOUS |
+                                   ME_CAPS_CTR_CLK_INTERNAL_1MHZ |
+                                   ME_CAPS_CTR_CLK_INTERNAL_10MHZ |
+                                   ME_CAPS_CTR_CLK_EXTERNAL;
+                       else
+                               subdevice->caps =
+                                   ME_CAPS_CTR_CLK_INTERNAL_1MHZ |
+                                   ME_CAPS_CTR_CLK_INTERNAL_10MHZ |
+                                   ME_CAPS_CTR_CLK_EXTERNAL;
+               } else
+                       subdevice->caps =
+                           ME_CAPS_CTR_CLK_PREVIOUS | ME_CAPS_CTR_CLK_EXTERNAL;
+
+               // Get the counters registers
+               subdevice->val_reg =
+                   me1400CD_get_val_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->ctrl_reg =
+                   me1400CD_get_ctrl_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->clk_src_reg =
+                   me1400CD_get_clk_src_reg(reg_base, me8254_idx, ctr_idx);
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4610:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4660IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4670IS:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680I:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680S:
+       case PCI_DEVICE_ID_MEILHAUS_ME4680IS:
+               // Check if 8254 index is out of range
+               if (me8254_idx > 0) {
+                       PERROR("8254 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+               // Initialize the counters capabilities
+               subdevice->caps = ME_CAPS_CTR_CLK_EXTERNAL;
+
+               // Get the counters registers
+               subdevice->val_reg =
+                   me4600_get_val_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->ctrl_reg =
+                   me4600_get_ctrl_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->clk_src_reg = 0;     // Not used
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_A:
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_B:
+               // Check if 8254 index is out of range
+               if (me8254_idx > 0) {
+                       PERROR("8254 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+               // Initialize the counters capabilities
+               subdevice->caps = ME_CAPS_CTR_CLK_EXTERNAL;
+
+               // Get the counters registers
+               subdevice->val_reg =
+                   me8100_get_val_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->ctrl_reg =
+                   me8100_get_ctrl_reg(reg_base, me8254_idx, ctr_idx);
+               subdevice->clk_src_reg = 0;     // Not used
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4650:
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+               PERROR("No 8254 subdevices available for subdevice device.\n");
+               me_subdevice_deinit(&subdevice->base);
+               kfree(subdevice);
+               return NULL;
+
+       default:
+               PERROR("Unknown device type.\n");
+               me_subdevice_deinit(&subdevice->base);
+               kfree(subdevice);
+               return NULL;
+       }
+
+       // Overload subdevice base class methods.
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8254_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config = me8254_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8254_io_single_read;
+       subdevice->base.me_subdevice_io_single_write = me8254_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8254_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8254_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8254_query_subdevice_caps;
+       subdevice->base.me_subdevice_query_subdevice_caps_args =
+           me8254_query_subdevice_caps_args;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me8254.h b/drivers/staging/meilhaus/me8254.h
new file mode 100644 (file)
index 0000000..572b719
--- /dev/null
@@ -0,0 +1,80 @@
+/**
+ * @file me8254.h
+ *
+ * @brief 8254 counter implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _ME8254_H_
+#define _ME8254_H_
+
+#include "mesubdevice.h"
+#include "meslock.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The 8254 subdevice class.
+ */
+typedef struct me8254_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect the control register from concurrent access. */
+       spinlock_t *clk_src_reg_lock;           /**< Spin lock to protect the clock source register from concurrent access. */
+
+       uint32_t device_id;                     /**< The Meilhaus device type carrying the 8254 chip. */
+       int me8254_idx;                         /**< The index of the 8254 chip on the device. */
+       int ctr_idx;                            /**< The index of the counter on the 8254 chip. */
+
+       int caps;                               /**< Holds the device capabilities. */
+
+       unsigned long val_reg;                  /**< Holds the actual counter value. */
+       unsigned long ctrl_reg;                 /**< Register to configure the 8254 modes. */
+       unsigned long clk_src_reg;              /**< Register to configure the counter connections. */
+} me8254_subdevice_t;
+
+/**
+ * @brief The constructor to generate a 8254 instance.
+ *
+ * @param device_id The kind of Meilhaus device holding the 8254.
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param me8254_idx The index of the 8254 chip on the Meilhaus device.
+ * @param ctr_idx The index of the counter inside a 8254 chip.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the 8254 control register from concurrent access.
+ * @param clk_src_reg_lock Pointer to spin lock protecting the clock source register from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8254_subdevice_t *me8254_constructor(uint32_t device_id,
+                                      uint32_t reg_base,
+                                      unsigned int me8254_idx,
+                                      unsigned int ctr_idx,
+                                      spinlock_t * ctrl_reg_lock,
+                                      spinlock_t * clk_src_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8254_reg.h b/drivers/staging/meilhaus/me8254_reg.h
new file mode 100644 (file)
index 0000000..7e2c36b
--- /dev/null
@@ -0,0 +1,172 @@
+/**
+ * @file me8254_reg.h
+ *
+ * @brief 8254 counter register definitions.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME8254_REG_H_
+#define _ME8254_REG_H_
+
+#ifdef __KERNEL__
+
+/* ME1400 A/B register offsets */
+#define ME1400AB_8254_A_0_VAL_REG              0x0004 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400AB_8254_A_1_VAL_REG              0x0005 /**< Offset of 8254 A counter 1 value register. */
+#define ME1400AB_8254_A_2_VAL_REG              0x0006 /**< Offset of 8254 A counter 2 value register. */
+#define ME1400AB_8254_A_CTRL_REG               0x0007 /**< Offset of 8254 A control register. */
+
+#define ME1400AB_8254_B_0_VAL_REG              0x000C /**< Offset of 8254 B counter 0 value register. */
+#define ME1400AB_8254_B_1_VAL_REG              0x000D /**< Offset of 8254 B counter 1 value register. */
+#define ME1400AB_8254_B_2_VAL_REG              0x000E /**< Offset of 8254 B counter 2 value register. */
+#define ME1400AB_8254_B_CTRL_REG               0x000F /**< Offset of 8254 B control register. */
+
+#define ME1400AB_CLK_SRC_REG                   0x0010 /**< Offset of clock source register. */
+
+/* ME1400 C register offsets */
+#define ME1400C_8254_A_0_VAL_REG               0x0004 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400C_8254_A_1_VAL_REG               0x0005 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400C_8254_A_2_VAL_REG               0x0006 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400C_8254_A_CTRL_REG                        0x0007 /**< Offset of 8254 A control register. */
+
+#define ME1400C_8254_B_0_VAL_REG               0x000C /**< Offset of 8254 B counter 0 value register. */
+#define ME1400C_8254_B_1_VAL_REG               0x000D /**< Offset of 8254 B counter 0 value register. */
+#define ME1400C_8254_B_2_VAL_REG               0x000E /**< Offset of 8254 B counter 0 value register. */
+#define ME1400C_8254_B_CTRL_REG                        0x000F /**< Offset of 8254 B control register. */
+
+#define ME1400C_8254_C_0_VAL_REG               0x0010 /**< Offset of 8254 C counter 0 value register. */
+#define ME1400C_8254_C_1_VAL_REG               0x0011 /**< Offset of 8254 C counter 0 value register. */
+#define ME1400C_8254_C_2_VAL_REG               0x0012 /**< Offset of 8254 C counter 0 value register. */
+#define ME1400C_8254_C_CTRL_REG                        0x0013 /**< Offset of 8254 C control register. */
+
+#define ME1400C_8254_D_0_VAL_REG               0x0014 /**< Offset of 8254 D counter 0 value register. */
+#define ME1400C_8254_D_1_VAL_REG               0x0015 /**< Offset of 8254 D counter 0 value register. */
+#define ME1400C_8254_D_2_VAL_REG               0x0016 /**< Offset of 8254 D counter 0 value register. */
+#define ME1400C_8254_D_CTRL_REG                        0x0017 /**< Offset of 8254 D control register. */
+
+#define ME1400C_8254_E_0_VAL_REG               0x0018 /**< Offset of 8254 E counter 0 value register. */
+#define ME1400C_8254_E_1_VAL_REG               0x0019 /**< Offset of 8254 E counter 0 value register. */
+#define ME1400C_8254_E_2_VAL_REG               0x001A /**< Offset of 8254 E counter 0 value register. */
+#define ME1400C_8254_E_CTRL_REG                        0x001B /**< Offset of 8254 E control register. */
+
+#define ME1400C_CLK_SRC_0_REG                          0x001C /**< Offset of clock source register 0. */
+#define ME1400C_CLK_SRC_1_REG                          0x001D /**< Offset of clock source register 1. */
+#define ME1400C_CLK_SRC_2_REG                          0x001E /**< Offset of clock source register 2. */
+
+/* ME1400 D register offsets */
+#define ME1400D_8254_A_0_VAL_REG               0x0044 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400D_8254_A_1_VAL_REG               0x0045 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400D_8254_A_2_VAL_REG               0x0046 /**< Offset of 8254 A counter 0 value register. */
+#define ME1400D_8254_A_CTRL_REG                        0x0047 /**< Offset of 8254 A control register. */
+
+#define ME1400D_8254_B_0_VAL_REG               0x004C /**< Offset of 8254 B counter 0 value register. */
+#define ME1400D_8254_B_1_VAL_REG               0x004D /**< Offset of 8254 B counter 0 value register. */
+#define ME1400D_8254_B_2_VAL_REG               0x004E /**< Offset of 8254 B counter 0 value register. */
+#define ME1400D_8254_B_CTRL_REG                        0x004F /**< Offset of 8254 B control register. */
+
+#define ME1400D_8254_C_0_VAL_REG               0x0050 /**< Offset of 8254 C counter 0 value register. */
+#define ME1400D_8254_C_1_VAL_REG               0x0051 /**< Offset of 8254 C counter 0 value register. */
+#define ME1400D_8254_C_2_VAL_REG               0x0052 /**< Offset of 8254 C counter 0 value register. */
+#define ME1400D_8254_C_CTRL_REG                        0x0053 /**< Offset of 8254 C control register. */
+
+#define ME1400D_8254_D_0_VAL_REG               0x0054 /**< Offset of 8254 D counter 0 value register. */
+#define ME1400D_8254_D_1_VAL_REG               0x0055 /**< Offset of 8254 D counter 0 value register. */
+#define ME1400D_8254_D_2_VAL_REG               0x0056 /**< Offset of 8254 D counter 0 value register. */
+#define ME1400D_8254_D_CTRL_REG                        0x0057 /**< Offset of 8254 D control register. */
+
+#define ME1400D_8254_E_0_VAL_REG               0x0058 /**< Offset of 8254 E counter 0 value register. */
+#define ME1400D_8254_E_1_VAL_REG               0x0059 /**< Offset of 8254 E counter 0 value register. */
+#define ME1400D_8254_E_2_VAL_REG               0x005A /**< Offset of 8254 E counter 0 value register. */
+#define ME1400D_8254_E_CTRL_REG                        0x005B /**< Offset of 8254 E control register. */
+
+#define ME1400D_CLK_SRC_0_REG                          0x005C /**< Offset of clock source register 0. */
+#define ME1400D_CLK_SRC_1_REG                          0x005D /**< Offset of clock source register 1. */
+#define ME1400D_CLK_SRC_2_REG                          0x005E /**< Offset of clock source register 2. */
+
+/* ME4600 register offsets */
+#define ME4600_8254_0_VAL_REG                  0x0000 /**< Offset of 8254 A counter 0 value register. */
+#define ME4600_8254_1_VAL_REG                  0x0001 /**< Offset of 8254 A counter 0 value register. */
+#define ME4600_8254_2_VAL_REG                  0x0002 /**< Offset of 8254 A counter 0 value register. */
+#define ME4600_8254_CTRL_REG                   0x0003 /**< Offset of 8254 A control register. */
+
+/* Command words for 8254 control register */
+#define ME8254_CTRL_SC0   0x00  /**< Counter 0 selection. */
+#define ME8254_CTRL_SC1   0x40  /**< Counter 1 selection. */
+#define ME8254_CTRL_SC2   0x80  /**< Counter 2 selection. */
+
+#define ME8254_CTRL_TLO   0x00  /**< Counter latching operation. */
+#define ME8254_CTRL_LSB   0x10  /**< Only read LSB. */
+#define ME8254_CTRL_MSB   0x20  /**< Only read MSB. */
+#define ME8254_CTRL_LM    0x30  /**< First read LSB, then MSB.  */
+
+#define ME8254_CTRL_M0    0x00  /**< Mode 0 selection. */
+#define ME8254_CTRL_M1    0x02  /**< Mode 1 selection. */
+#define ME8254_CTRL_M2    0x04  /**< Mode 2 selection. */
+#define ME8254_CTRL_M3    0x06  /**< Mode 3 selection. */
+#define ME8254_CTRL_M4    0x08  /**< Mode 4 selection. */
+#define ME8254_CTRL_M5    0x0A  /**< Mode 5 selection. */
+
+#define ME8254_CTRL_BIN   0x00  /**< Binary counter. */
+#define ME8254_CTRL_BCD   0x01  /**< BCD counter. */
+
+/* ME-1400 A/B clock source register bits */
+#define ME1400AB_8254_A_0_CLK_SRC_1MHZ         (0 << 7)        /**< 1MHz clock. */
+#define ME1400AB_8254_A_0_CLK_SRC_10MHZ                (1 << 7)        /**< 10MHz clock. */
+#define ME1400AB_8254_A_0_CLK_SRC_PIN          (0 << 6)        /**< CLK 0 to SUB-D. */
+#define ME1400AB_8254_A_0_CLK_SRC_QUARZ                (1 << 6)        /**< Connect CLK 0 with quarz. */
+
+#define ME1400AB_8254_A_1_CLK_SRC_PIN          (0 << 5)        /**< CLK 1 to SUB-D. */
+#define ME1400AB_8254_A_1_CLK_SRC_PREV         (1 << 5)        /**< Connect OUT 0 with CLK 1. */
+
+#define ME1400AB_8254_A_2_CLK_SRC_PIN          (0 << 4)        /**< CLK 2 to SUB-D. */
+#define ME1400AB_8254_A_2_CLK_SRC_PREV         (1 << 4)        /**< Connect OUT 1 with CLK 2. */
+
+#define ME1400AB_8254_B_0_CLK_SRC_1MHZ         (0 << 3)        /**< 1MHz clock. */
+#define ME1400AB_8254_B_0_CLK_SRC_10MHZ                (1 << 3)        /**< 10MHz clock. */
+#define ME1400AB_8254_B_0_CLK_SRC_PIN          (0 << 2)        /**< CLK 0 to SUB-D. */
+#define ME1400AB_8254_B_0_CLK_SRC_QUARZ                (1 << 2)        /**< Connect CLK 0 with quarz. */
+
+#define ME1400AB_8254_B_1_CLK_SRC_PIN          (0 << 1)        /**< CLK 1 to SUB-D. */
+#define ME1400AB_8254_B_1_CLK_SRC_PREV         (1 << 1)        /**< Connect OUT 0 with CLK 1. */
+
+#define ME1400AB_8254_B_2_CLK_SRC_PIN          (0 << 0)        /**< CLK 2 to SUB-D. */
+#define ME1400AB_8254_B_2_CLK_SRC_PREV         (1 << 0)        /**< Connect OUT 1 with CLK 2. */
+
+/* ME-1400 C/D clock source registers bits */
+#define ME1400CD_8254_ACE_0_CLK_SRC_MASK       0x03    /**< Masks all CLK source bits. */
+#define ME1400CD_8254_ACE_0_CLK_SRC_PIN                0x00    /**< Connect CLK to SUB-D. */
+#define ME1400CD_8254_ACE_0_CLK_SRC_1MHZ       0x01    /**< Connect CLK to 1MHz. */
+#define ME1400CD_8254_ACE_0_CLK_SRC_10MHZ      0x02    /**< Connect CLK to 10MHz. */
+#define ME1400CD_8254_ACE_0_CLK_SRC_PREV       0x03    /**< Connect CLK to previous counter output on ME-1400 D extension. */
+
+#define ME1400CD_8254_ACE_1_CLK_SRC_MASK       0x04    /**< Masks all CLK source bits. */
+#define ME1400CD_8254_ACE_1_CLK_SRC_PIN                0x00    /**< Connect CLK to SUB-D. */
+#define ME1400CD_8254_ACE_1_CLK_SRC_PREV       0x04    /**< Connect CLK to previous counter output. */
+
+#define ME1400CD_8254_ACE_2_CLK_SRC_MASK       0x08    /**< Masks all CLK source bits. */
+#define ME1400CD_8254_ACE_2_CLK_SRC_PIN                0x00    /**< Connect to SUB-D. */
+#define ME1400CD_8254_ACE_2_CLK_SRC_PREV       0x08    /**< Connect CLK to previous counter output. */
+
+#define ME1400CD_8254_BD_0_CLK_SRC_MASK                0x30    /**< Masks all CLK source bits. */
+#define ME1400CD_8254_BD_0_CLK_SRC_PIN         0x00    /**< Connect CLK to SUB-D. */
+#define ME1400CD_8254_BD_0_CLK_SRC_1MHZ                0x10    /**< Connect CLK to 1MHz. */
+#define ME1400CD_8254_BD_0_CLK_SRC_10MHZ       0x20    /**< Connect CLK to 10MHz. */
+#define ME1400CD_8254_BD_0_CLK_SRC_PREV                0x30    /**< Connect CLK to previous counter output. */
+
+#define ME1400CD_8254_BD_1_CLK_SRC_MASK                0x40    /**< Masks all CLK source bits. */
+#define ME1400CD_8254_BD_1_CLK_SRC_PIN         0x00    /**< Connect CLK to SUB-D. */
+#define ME1400CD_8254_BD_1_CLK_SRC_PREV                0x40    /**< Connect CLK to previous counter output. */
+
+#define ME1400CD_8254_BD_2_CLK_SRC_MASK                0x80    /**< Masks all CLK source bits. */
+#define ME1400CD_8254_BD_2_CLK_SRC_PIN         0x00    /**< Connect CLK to SUB-D. */
+#define ME1400CD_8254_BD_2_CLK_SRC_PREV                0x80    /**< Connect CLK to previous counter output. */
+
+/* ME-8100 counter registers */
+#define ME8100_COUNTER_REG_0                           0x18    //(r,w)
+#define ME8100_COUNTER_REG_1                           0x1A    //(r,w)
+#define ME8100_COUNTER_REG_2                           0x1C    //(r,w)
+#define ME8100_COUNTER_CTRL_REG                                0x1E    //(r,w)
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8255.c b/drivers/staging/meilhaus/me8255.c
new file mode 100644 (file)
index 0000000..180e7f8
--- /dev/null
@@ -0,0 +1,462 @@
+/**
+ * @file me8255.c
+ *
+ * @brief 8255 subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+#include "medebug.h"
+
+#include "me8255_reg.h"
+#include "me8255.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static uint8_t get_mode_from_mirror(uint32_t mirror)
+{
+       PDEBUG("executed.\n");
+
+       if (mirror & ME8255_PORT_0_OUTPUT) {
+               if (mirror & ME8255_PORT_1_OUTPUT) {
+                       if (mirror & ME8255_PORT_2_OUTPUT) {
+                               return ME8255_MODE_OOO;
+                       } else {
+                               return ME8255_MODE_IOO;
+                       }
+               } else {
+                       if (mirror & ME8255_PORT_2_OUTPUT) {
+                               return ME8255_MODE_OIO;
+                       } else {
+                               return ME8255_MODE_IIO;
+                       }
+               }
+       } else {
+               if (mirror & ME8255_PORT_1_OUTPUT) {
+                       if (mirror & ME8255_PORT_2_OUTPUT) {
+                               return ME8255_MODE_OOI;
+                       } else {
+                               return ME8255_MODE_IOI;
+                       }
+               } else {
+                       if (mirror & ME8255_PORT_2_OUTPUT) {
+                               return ME8255_MODE_OII;
+                       } else {
+                               return ME8255_MODE_III;
+                       }
+               }
+       }
+}
+
+static int me8255_io_reset_subdevice(struct me_subdevice *subdevice,
+                                    struct file *filep, int flags)
+{
+       me8255_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8255_subdevice_t *) subdevice;
+
+       if (flags) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       spin_lock(instance->ctrl_reg_lock);
+       *instance->ctrl_reg_mirror &=
+           ~(ME8255_PORT_0_OUTPUT << instance->dio_idx);
+       outb(get_mode_from_mirror(*instance->ctrl_reg_mirror),
+            instance->ctrl_reg);
+       spin_unlock(instance->ctrl_reg_lock);
+
+       outb(0, instance->port_reg);
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8255_io_single_config(struct me_subdevice *subdevice,
+                                  struct file *filep,
+                                  int channel,
+                                  int single_config,
+                                  int ref,
+                                  int trig_chan,
+                                  int trig_type, int trig_edge, int flags)
+{
+       me8255_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8255_subdevice_t *) subdevice;
+
+       if (flags & ~ME_IO_SINGLE_CONFIG_DIO_BYTE) {
+               PERROR("Invalid flag specified.\n");
+               return ME_ERRNO_INVALID_FLAGS;
+       }
+
+       if (channel) {
+               PERROR("Invalid channel.\n");
+               return ME_ERRNO_INVALID_CHANNEL;
+       }
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
+               spin_lock(instance->ctrl_reg_lock);
+               *instance->ctrl_reg_mirror &=
+                   ~(ME8255_PORT_0_OUTPUT << instance->dio_idx);
+               outb(get_mode_from_mirror(*instance->ctrl_reg_mirror),
+                    instance->ctrl_reg);
+               spin_unlock(instance->ctrl_reg_lock);
+       } else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
+               spin_lock(instance->ctrl_reg_lock);
+               *instance->ctrl_reg_mirror |=
+                   (ME8255_PORT_0_OUTPUT << instance->dio_idx);
+               outb(get_mode_from_mirror(*instance->ctrl_reg_mirror),
+                    instance->ctrl_reg);
+               spin_unlock(instance->ctrl_reg_lock);
+       } else {
+               PERROR("Invalid port direction.\n");
+               err = ME_ERRNO_INVALID_SINGLE_CONFIG;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8255_io_single_read(struct me_subdevice *subdevice,
+                                struct file *filep,
+                                int channel,
+                                int *value, int time_out, int flags)
+{
+       me8255_subdevice_t *instance;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8255_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       *value = inb(instance->port_reg) & (0x1 << channel);
+               } else {
+                       PERROR("Invalid bit number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       *value = inb(instance->port_reg);
+               } else {
+                       PERROR("Invalid byte number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8255_io_single_write(struct me_subdevice *subdevice,
+                                 struct file *filep,
+                                 int channel,
+                                 int value, int time_out, int flags)
+{
+       me8255_subdevice_t *instance;
+       uint8_t byte;
+       int err = ME_ERRNO_SUCCESS;
+
+       PDEBUG("executed.\n");
+
+       instance = (me8255_subdevice_t *) subdevice;
+
+       ME_SUBDEVICE_ENTER;
+
+       spin_lock(&instance->subdevice_lock);
+       switch (flags) {
+       case ME_IO_SINGLE_TYPE_DIO_BIT:
+               if ((channel >= 0) && (channel < 8)) {
+                       if (*instance->
+                           ctrl_reg_mirror & (ME8255_PORT_0_OUTPUT <<
+                                              instance->dio_idx)) {
+                               byte = inb(instance->port_reg);
+
+                               if (value)
+                                       byte |= 0x1 << channel;
+                               else
+                                       byte &= ~(0x1 << channel);
+
+                               outb(byte, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid bit number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       case ME_IO_SINGLE_NO_FLAGS:
+       case ME_IO_SINGLE_TYPE_DIO_BYTE:
+               if (channel == 0) {
+                       if (*instance->
+                           ctrl_reg_mirror & (ME8255_PORT_0_OUTPUT <<
+                                              instance->dio_idx)) {
+                               outb(value, instance->port_reg);
+                       } else {
+                               PERROR("Port not in output mode.\n");
+                               err = ME_ERRNO_PREVIOUS_CONFIG;
+                       }
+               } else {
+                       PERROR("Invalid byte number.\n");
+                       err = ME_ERRNO_INVALID_CHANNEL;
+               }
+               break;
+
+       default:
+               PERROR("Invalid flags specified.\n");
+               err = ME_ERRNO_INVALID_FLAGS;
+       }
+       spin_unlock(&instance->subdevice_lock);
+
+       ME_SUBDEVICE_EXIT;
+
+       return err;
+}
+
+static int me8255_query_number_channels(struct me_subdevice *subdevice,
+                                       int *number)
+{
+       PDEBUG("executed.\n");
+       *number = ME8255_NUMBER_CHANNELS;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8255_query_subdevice_type(struct me_subdevice *subdevice,
+                                      int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = ME_TYPE_DIO;
+       *subtype = ME_SUBTYPE_SINGLE;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me8255_query_subdevice_caps(struct me_subdevice *subdevice,
+                                      int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = ME_CAPS_DIO_DIR_BYTE;
+       return ME_ERRNO_SUCCESS;
+}
+
+me8255_subdevice_t *me8255_constructor(uint32_t device_id,
+                                      uint32_t reg_base,
+                                      unsigned int me8255_idx,
+                                      unsigned int dio_idx,
+                                      int *ctrl_reg_mirror,
+                                      spinlock_t * ctrl_reg_lock)
+{
+       me8255_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(me8255_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for 8255 instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(me8255_subdevice_t));
+
+       /* Check if counter index is out of range */
+
+       if (dio_idx > 2) {
+               PERROR("DIO index is out of range.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the pointer to global port settings */
+       subdevice->ctrl_reg_mirror = ctrl_reg_mirror;
+
+       /* Save type of Meilhaus device */
+       subdevice->device_id = device_id;
+
+       /* Save the indices */
+       subdevice->me8255_idx = me8255_idx;
+       subdevice->dio_idx = dio_idx;
+
+       /* Do device specific initialization */
+       switch (device_id) {
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+               /* Check if 8255 index is out of range */
+               if (me8255_idx > 0) {
+                       PERROR("8255 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:     /* Fall through */
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               /* Check if 8255 index is out of range */
+               if (me8255_idx > 1) {
+                       PERROR("8255 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+               /* Get the registers */
+               if (me8255_idx == 0) {
+                       subdevice->ctrl_reg = reg_base + ME1400AB_PORT_A_CTRL;
+                       subdevice->port_reg =
+                           reg_base + ME1400AB_PORT_A_0 + dio_idx;
+               } else if (me8255_idx == 1) {
+                       subdevice->ctrl_reg = reg_base + ME1400AB_PORT_B_CTRL;
+                       subdevice->port_reg =
+                           reg_base + ME1400AB_PORT_B_0 + dio_idx;
+               }
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+               /* Check if 8255 index is out of range */
+               if (me8255_idx > 0) {
+                       PERROR("8255 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:     /* Fall through */
+               /* Check if 8255 index is out of range */
+               if (me8255_idx > 1) {
+                       PERROR("8255 index is out of range.\n");
+                       me_subdevice_deinit(&subdevice->base);
+                       kfree(subdevice);
+                       return NULL;
+               }
+
+               /* Get the registers */
+               if (me8255_idx == 0) {
+                       subdevice->ctrl_reg = reg_base + ME1400CD_PORT_A_CTRL;
+                       subdevice->port_reg =
+                           reg_base + ME1400CD_PORT_A_0 + dio_idx;
+               } else if (me8255_idx == 1) {
+                       subdevice->ctrl_reg = reg_base + ME1400CD_PORT_B_CTRL;
+                       subdevice->port_reg =
+                           reg_base + ME1400CD_PORT_B_0 + dio_idx;
+               }
+
+               break;
+
+       default:
+               PERROR("Unknown device type. dev ID: 0x%04x\n", device_id);
+
+               me_subdevice_deinit(&subdevice->base);
+
+               kfree(subdevice);
+
+               return NULL;
+       }
+
+       /* Overload subdevice base class methods. */
+       subdevice->base.me_subdevice_io_reset_subdevice =
+           me8255_io_reset_subdevice;
+       subdevice->base.me_subdevice_io_single_config = me8255_io_single_config;
+       subdevice->base.me_subdevice_io_single_read = me8255_io_single_read;
+       subdevice->base.me_subdevice_io_single_write = me8255_io_single_write;
+       subdevice->base.me_subdevice_query_number_channels =
+           me8255_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           me8255_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           me8255_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/me8255.h b/drivers/staging/meilhaus/me8255.h
new file mode 100644 (file)
index 0000000..3382300
--- /dev/null
@@ -0,0 +1,59 @@
+/**
+ * @file me8255.h
+ *
+ * @brief Meilhaus PIO 8255 implementation.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME8255_H_
+#define _ME8255_H_
+
+#include "mesubdevice.h"
+#include "meslock.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The 8255 subdevice class.
+ */
+typedef struct me8255_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+
+       int *ctrl_reg_mirror;                   /**< Pointer to mirror of the control register. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg and #ctrl_reg_mirror from concurrent access. */
+
+       uint32_t device_id;                             /**< The PCI device id of the device holding the 8255 chip. */
+       int me8255_idx;                                 /**< The index of the 8255 chip on the device. */
+       int dio_idx;                                    /**< The index of the DIO port on the 8255 chip. */
+
+       unsigned long port_reg;                 /**< Register to read or write a value from or to the port respectively. */
+       unsigned long ctrl_reg;                 /**< Register to configure the 8255 modes. */
+} me8255_subdevice_t;
+
+/**
+ * @brief The constructor to generate a 8255 instance.
+ *
+ * @param device_id The kind of Meilhaus device holding the 8255.
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param me8255_idx The index of the 8255 chip on the Meilhaus device.
+ * @param dio_idx The index of the counter inside a 8255 chip.
+ * @param ctr_reg_mirror Pointer to mirror of control register.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the 8255 control register and #ctrl_reg_mirror from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+me8255_subdevice_t *me8255_constructor(uint32_t device_id,
+                                      uint32_t reg_base,
+                                      unsigned int me8255_idx,
+                                      unsigned int dio_idx,
+                                      int *ctrl_reg_mirror,
+                                      spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/me8255_reg.h b/drivers/staging/meilhaus/me8255_reg.h
new file mode 100644 (file)
index 0000000..d1dea1a
--- /dev/null
@@ -0,0 +1,50 @@
+/**
+ * @file me8255_reg.h
+ *
+ * @brief 8255 counter register definitions.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME8255_REG_H_
+#define _ME8255_REG_H_
+
+#ifdef __KERNEL__
+
+#define ME8255_NUMBER_CHANNELS         8               /**< The number of channels per 8255 port. */
+
+#define ME1400AB_PORT_A_0                      0x0000  /**< Port 0 offset. */
+#define ME1400AB_PORT_A_1                      0x0001  /**< Port 1 offset. */
+#define ME1400AB_PORT_A_2                      0x0002  /**< Port 2 offset. */
+#define ME1400AB_PORT_A_CTRL           0x0003  /**< Control register for 8255 A. */
+
+#define ME1400AB_PORT_B_0                      0x0008  /**< Port 0 offset. */
+#define ME1400AB_PORT_B_1                      0x0009  /**< Port 1 offset. */
+#define ME1400AB_PORT_B_2                      0x000A  /**< Port 2 offset. */
+#define ME1400AB_PORT_B_CTRL           0x000B  /**< Control register for 8255 B. */
+
+#define ME1400CD_PORT_A_0                      0x0000  /**< Port 0 offset. */
+#define ME1400CD_PORT_A_1                      0x0001  /**< Port 1 offset. */
+#define ME1400CD_PORT_A_2                      0x0002  /**< Port 2 offset. */
+#define ME1400CD_PORT_A_CTRL           0x0003  /**< Control register for 8255 A. */
+
+#define ME1400CD_PORT_B_0                      0x0040  /**< Port 0 offset. */
+#define ME1400CD_PORT_B_1                      0x0041  /**< Port 1 offset. */
+#define ME1400CD_PORT_B_2                      0x0042  /**< Port 2 offset. */
+#define ME1400CD_PORT_B_CTRL           0x0043  /**< Control register for 8255 B. */
+
+#define ME8255_MODE_OOO                                0x80    /**< Port 2 = Output, Port 1 = Output, Port 0 = Output */
+#define ME8255_MODE_IOO                                0x89    /**< Port 2 = Input,  Port 1 = Output, Port 0 = Output */
+#define ME8255_MODE_OIO                                0x82    /**< Port 2 = Output, Port 1 = Input,  Port 0 = Output */
+#define ME8255_MODE_IIO                                0x8B    /**< Port 2 = Input,  Port 1 = Input,  Port 0 = Output */
+#define ME8255_MODE_OOI                                0x90    /**< Port 2 = Output, Port 1 = Output, Port 0 = Input */
+#define ME8255_MODE_IOI                                0x99    /**< Port 2 = Input,  Port 1 = Output, Port 0 = Input */
+#define ME8255_MODE_OII                                0x92    /**< Port 2 = Output, Port 1 = Input,  Port 0 = Input */
+#define ME8255_MODE_III                                0x9B    /**< Port 2 = Input,  Port 1 = Input,  Port 0 = Input */
+
+#define ME8255_PORT_0_OUTPUT           0x1             /**< If set in mirror then port 0 is in output mode. */
+#define ME8255_PORT_1_OUTPUT           0x2             /**< If set in mirror then port 1 is in output mode. */
+#define ME8255_PORT_2_OUTPUT           0x4             /**< If set in mirror then port 2 is in output mode. */
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/mecirc_buf.h b/drivers/staging/meilhaus/mecirc_buf.h
new file mode 100644 (file)
index 0000000..e9b591e
--- /dev/null
@@ -0,0 +1,131 @@
+/**
+ * @file mecirc_buf.h
+ *
+ * @brief Meilhaus circular buffer implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke  (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _MECIRC_BUF_H_
+#define _MECIRC_BUF_H_
+
+# ifdef __KERNEL__
+
+#  ifdef BOSCH
+
+typedef struct me_circ_buf {
+       unsigned int mask;
+//      unsigned int count;
+       uint32_t *buf;
+       int volatile head;
+       int volatile tail;
+} me_circ_buf_t;
+
+static int inline me_circ_buf_values(me_circ_buf_t * buf)
+{
+//      return ((buf->head - buf->tail) & (buf->count - 1));
+       return ((buf->head - buf->tail) & (buf->mask));
+}
+
+static int inline me_circ_buf_space(me_circ_buf_t * buf)
+{
+//      return ((buf->tail - (buf->head + 1)) & (buf->count - 1));
+       return ((buf->tail - (buf->head + 1)) & (buf->mask));
+}
+
+static int inline me_circ_buf_values_to_end(me_circ_buf_t * buf)
+{
+       int end;
+       int n;
+//      end = buf->count - buf->tail;
+//      n = (buf->head + end) & (buf->count - 1);
+       end = buf->mask + 1 - buf->tail;
+       n = (buf->head + end) & (buf->mask);
+       return (n < end) ? n : end;
+}
+
+static int inline me_circ_buf_space_to_end(me_circ_buf_t * buf)
+{
+       int end;
+       int n;
+
+//      end = buf->count - 1 - buf->head;
+//      n = (end + buf->tail) & (buf->count - 1);
+       end = buf->mask - buf->head;
+       n = (end + buf->tail) & (buf->mask);
+       return (n <= end) ? n : (end + 1);
+}
+
+#define _CBUFF_32b_t
+
+#  else        //~BOSCH
+/// @note buf->mask = buf->count-1 = ME4600_AI_CIRC_BUF_COUNT-1
+
+#   ifdef _CBUFF_32b_t
+       //32 bit
+typedef struct me_circ_buf_32b {
+       int volatile head;
+       int volatile tail;
+       unsigned int mask;      //buffor size-1 must be 2^n-1 to work
+       uint32_t *buf;
+} me_circ_buf_t;
+#   else
+       //16 bit
+typedef struct me_circ_buf_16b {
+       int volatile head;
+       int volatile tail;
+       unsigned int mask;      //buffor size-1 must be 2^n-1 to work
+       uint16_t *buf;
+} me_circ_buf_t;
+#   endif //_CBUFF_32b_t
+
+/** How many values is in buffer */
+static int inline me_circ_buf_values(me_circ_buf_t * buf)
+{
+       return ((buf->head - buf->tail) & (buf->mask));
+}
+
+/** How many space left */
+static int inline me_circ_buf_space(me_circ_buf_t * buf)
+{
+       return ((buf->tail - (buf->head + 1)) & (buf->mask));
+}
+
+/** How many values can be read from buffor in one chunck. */
+static int inline me_circ_buf_values_to_end(me_circ_buf_t * buf)
+{
+       return (buf->tail <=
+               buf->head) ? (buf->head - buf->tail) : (buf->mask - buf->tail +
+                                                       1);
+}
+
+/** How many values can be write to buffer in one chunck. */
+static int inline me_circ_buf_space_to_end(me_circ_buf_t * buf)
+{
+       return (buf->tail <=
+               buf->head) ? (buf->mask - buf->head + 1) : (buf->tail -
+                                                           buf->head - 1);
+}
+
+#  endif //BOSCH
+# endif        //__KERNEL__
+#endif //_MECIRC_BUF_H_
diff --git a/drivers/staging/meilhaus/mecommon.h b/drivers/staging/meilhaus/mecommon.h
new file mode 100644 (file)
index 0000000..ef47c38
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File :mecommon.h
+ * Author      :GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ * Author      :KG (Krzysztof Gantzke) <k.gantzke@meilhaus.de>
+ */
+
+#ifndef _MECOMMON_H_
+#define _MECOMMON_H_
+
+/*==================================================================
+  The version of this release
+  ================================================================*/
+
+#ifndef ME_VERSION_DRIVER
+/* Unknown version */
+# define ME_VERSION_DRIVER     0xFFFFFFFF
+#endif
+
+#ifndef LIBMEDRIVER_VERSION
+/* Unknown version */
+# define LIBMEDRIVER_VERSION   0xFFFFFFFF
+#endif
+
+#endif
diff --git a/drivers/staging/meilhaus/medebug.h b/drivers/staging/meilhaus/medebug.h
new file mode 100644 (file)
index 0000000..382d00f
--- /dev/null
@@ -0,0 +1,125 @@
+/**
+ * @file medebug.h
+ *
+ * @brief Debugging defines.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+#ifndef _MEDEBUG_H_
+#define _MEDEBUG_H_
+
+#ifdef __KERNEL__
+
+#include <linux/kernel.h>
+
+//Messages control.
+
+#ifdef MEDEBUG_TEST_ALL                /* Switch to enable all info messages. */
+# ifndef MEDEBUG_TEST
+#  define MEDEBUG_TEST
+# endif
+# ifndef MEDEBUG_TEST_INFO
+#  define MEDEBUG_TEST_INFO
+# endif
+# ifndef MEDEBUG_DEBUG_REG
+#  define MEDEBUG_DEBUG_REG    /* Switch to enable registry access debuging messages. */
+# endif
+# ifndef MEDEBUG_DEBUG_LOCKS
+#  define MEDEBUG_DEBUG_LOCKS  /* Switch to enable locking messages. */
+# endif
+#endif
+
+#ifdef MEDEBUG_TEST_INFO       /* Switch to enable info  and test messages. */
+# ifndef MEDEBUG_INFO
+#  define MEDEBUG_INFO         /* Switch to enable info messages. */
+# endif
+# ifndef MEDEBUG_TEST
+#  define MEDEBUG_TEST
+# endif
+#endif
+
+#ifdef MEDEBUG_TEST            /* Switch to enable debug test messages. */
+# ifndef MEDEBUG_DEBUG
+#  define MEDEBUG_DEBUG                /* Switch to enable debug messages. */
+# endif
+# ifndef MEDEBUG_ERROR
+#  define MEDEBUG_ERROR                /* Switch to enable error messages. */
+# endif
+#endif
+
+#ifdef MEDEBUG_ERROR           /* Switch to enable error messages. */
+# ifndef MEDEBUG_ERROR_CRITICAL        /* Also critical error messages. */
+#  define MEDEBUG_ERROR_CRITICAL       /* Switch to enable high importance error messages. */
+# endif
+#endif
+
+#undef PDEBUG                  /* Only to be sure. */
+#undef PINFO                   /* Only to be sure. */
+#undef PERROR                  /* Only to be sure. */
+#undef PERROR_CRITICAL         /* Only to be sure. */
+#undef PDEBUG_REG              /* Only to be sure. */
+#undef PDEBUG_LOCKS            /* Only to be sure. */
+#undef PSECURITY               /* Only to be sure. */
+#undef PLOG                    /* Only to be sure. */
+
+#ifdef MEDEBUG_DEBUG
+# define PDEBUG(fmt, args...) \
+       printk(KERN_DEBUG"ME_DRV D: <%s> " fmt, __FUNCTION__, ##args)
+#else
+# define PDEBUG(fmt, args...)
+#endif
+
+#ifdef MEDEBUG_DEBUG_LOCKS
+# define PDEBUG_LOCKS(fmt, args...) \
+       printk(KERN_DEBUG"ME_DRV L: <%s> " fmt, __FUNCTION__, ##args)
+#else
+# define PDEBUG_LOCKS(fmt, args...)
+#endif
+
+#ifdef MEDEBUG_DEBUG_REG
+# define PDEBUG_REG(fmt, args...) \
+       printk(KERN_DEBUG"ME_DRV R: <%s:%d> REG:" fmt, __FUNCTION__, __LINE__, ##args)
+#else
+# define PDEBUG_REG(fmt, args...)
+#endif
+
+#ifdef MEDEBUG_INFO
+# define PINFO(fmt, args...) \
+       printk(KERN_INFO"ME_DRV I: " fmt, ##args)
+#else
+# define PINFO(fmt, args...)
+#endif
+
+#ifdef MEDEBUG_ERROR
+# define PERROR(fmt, args...) \
+       printk(KERN_ERR"ME_DRV E: <%s:%i> " fmt, __FILE__, __LINE__, ##args)
+#else
+# define PERROR(fmt, args...)
+#endif
+
+#ifdef MEDEBUG_ERROR_CRITICAL
+# define PERROR_CRITICAL(fmt, args...) \
+       printk(KERN_CRIT"ME_DRV C: <%s:%i> " fmt, __FILE__, __LINE__, ##args)
+#else
+# define PERROR_CRITICAL(fmt, args...)
+#endif
+
+//This debug is only to detect logical errors!
+# define PSECURITY(fmt, args...) \
+       printk(KERN_CRIT"ME_DRV SECURITY: <%s:%s:%i> " fmt, __FILE__, __FUNCTION__, __LINE__, ##args)
+//This debug is to keep track in customers' system
+# define PLOG(fmt, args...) \
+       printk(KERN_INFO"ME_DRV: " fmt, ##args)
+
+//This debug is to check new parts during development
+#ifdef MEDEBUG_DEVELOP
+# define PDEVELOP(fmt, args...) \
+       printk(KERN_CRIT"ME_DRV: <%s:%s:%i> " fmt, __FILE__, __FUNCTION__, __LINE__, ##args)
+#else
+# define PDEVELOP(fmt, args...)
+#endif
+
+#endif //__KERNEL__
+#endif //_MEDEBUG_H_
diff --git a/drivers/staging/meilhaus/medefines.h b/drivers/staging/meilhaus/medefines.h
new file mode 100644 (file)
index 0000000..6158ef5
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : medefines.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ * Author      : KG (Krzysztof Gantzke)  <k.gantzke@meilhaus.de>
+ */
+
+#ifndef _MEDEFINES_H_
+#define _MEDEFINES_H_
+
+/*==================================================================
+  General
+  ================================================================*/
+
+#define ME_VALUE_NOT_USED                                                      0x0
+#define ME_VALUE_INVALID                                                       ~0x0
+
+/*==================================================================
+  Defines common to access functions
+  ================================================================*/
+
+#define ME_LOCK_RELEASE                                                                0x00010001
+#define ME_LOCK_SET                                                                    0x00010002
+#define ME_LOCK_CHECK                                                          0x00010003
+
+/*==================================================================
+  Defines meOpen function
+  ================================================================*/
+
+#define ME_OPEN_NO_FLAGS                                                       0x0
+
+/*==================================================================
+  Defines meClose function
+  ================================================================*/
+
+#define ME_CLOSE_NO_FLAGS                                                      0x0
+
+/*==================================================================
+  Defines meLockDriver function
+  ================================================================*/
+
+#define ME_LOCK_DRIVER_NO_FLAGS                                                0x0
+
+/*==================================================================
+  Defines meLockDevice function
+  ================================================================*/
+
+#define ME_LOCK_DEVICE_NO_FLAGS                                                0x0
+
+/*==================================================================
+  Defines meLockSubdevice function
+  ================================================================*/
+
+#define ME_LOCK_SUBDEVICE_NO_FLAGS                                     0x0
+
+
+/*==================================================================
+  Defines common to error functions
+  ================================================================*/
+
+#define ME_ERROR_MSG_MAX_COUNT                                         256
+
+#define ME_SWITCH_DISABLE                                                      0x00020001
+#define ME_SWITCH_ENABLE                                                       0x00020002
+
+/*==================================================================
+  Defines common to io functions
+  ================================================================*/
+
+#define ME_REF_DIO_FIFO_LOW                                                    0x00030001
+#define ME_REF_DIO_FIFO_HIGH                                           0x00030002
+
+#define ME_REF_CTR_PREVIOUS                                                    0x00040001
+#define ME_REF_CTR_INTERNAL_1MHZ                                       0x00040002
+#define ME_REF_CTR_INTERNAL_10MHZ                                      0x00040003
+#define ME_REF_CTR_EXTERNAL                                                    0x00040004
+
+#define ME_REF_AI_GROUND                                                       0x00050001
+#define ME_REF_AI_DIFFERENTIAL                                         0x00050002
+
+#define ME_REF_AO_GROUND                                                       0x00060001
+
+#define ME_TRIG_CHAN_DEFAULT                                           0x00070001
+#define ME_TRIG_CHAN_SYNCHRONOUS                                       0x00070002
+
+#define ME_TRIG_TYPE_NONE                                                      0x00000000
+#define ME_TRIG_TYPE_SW                                                                0x00080001
+#define ME_TRIG_TYPE_THRESHOLD                                         0x00080002
+#define ME_TRIG_TYPE_WINDOW                                                    0x00080003
+#define ME_TRIG_TYPE_EDGE                                                      0x00080004
+#define ME_TRIG_TYPE_SLOPE                                                     0x00080005
+#define ME_TRIG_TYPE_EXT_DIGITAL                                       0x00080006
+#define ME_TRIG_TYPE_EXT_ANALOG                                                0x00080007
+#define ME_TRIG_TYPE_PATTERN                                           0x00080008
+#define ME_TRIG_TYPE_TIMER                                                     0x00080009
+#define ME_TRIG_TYPE_COUNT                                                     0x0008000A
+#define ME_TRIG_TYPE_FOLLOW                                                    0x0008000B
+
+#define ME_TRIG_EDGE_NONE                                                      0x00000000
+#define ME_TRIG_EDGE_ABOVE                                                     0x00090001
+#define ME_TRIG_EDGE_BELOW                                                     0x00090002
+#define ME_TRIG_EDGE_ENTRY                                                     0x00090003
+#define ME_TRIG_EDGE_EXIT                                                      0x00090004
+#define ME_TRIG_EDGE_RISING                                                    0x00090005
+#define ME_TRIG_EDGE_FALLING                                           0x00090006
+#define ME_TRIG_EDGE_ANY                                                       0x00090007
+
+#define ME_TIMER_ACQ_START                                                     0x000A0001
+#define ME_TIMER_SCAN_START                                                    0x000A0002
+#define ME_TIMER_CONV_START                                                    0x000A0003
+
+/*==================================================================
+  Defines for meIOFrequencyToTicks function
+  ================================================================*/
+
+#define ME_IO_FREQUENCY_TO_TICKS_NO_FLAGS                      0x0
+
+/*==================================================================
+  Defines for meIOIrqStart function
+  ================================================================*/
+
+#define ME_IRQ_SOURCE_DIO_PATTERN                                      0x000B0001
+#define ME_IRQ_SOURCE_DIO_MASK                                         0x000B0002
+#define ME_IRQ_SOURCE_DIO_LINE                                         0x000B0003
+#define ME_IRQ_SOURCE_DIO_OVER_TEMP                                    0x000B0004
+
+#define ME_IRQ_EDGE_NOT_USED                                           0x00000000
+#define ME_IRQ_EDGE_RISING                                                     0x000C0001
+#define ME_IRQ_EDGE_FALLING                                                    0x000C0002
+#define ME_IRQ_EDGE_ANY                                                                0x000C0003
+
+/*==================================================================
+  Defines for meIOIrqStart function
+  ================================================================*/
+
+#define ME_IO_IRQ_START_NO_FLAGS                                       0x000000
+#define ME_IO_IRQ_START_DIO_BIT                                                0x000001
+#define ME_IO_IRQ_START_DIO_BYTE                                       0x000002
+#define ME_IO_IRQ_START_DIO_WORD                                       0x000004
+#define ME_IO_IRQ_START_DIO_DWORD                                      0x000008
+#define ME_IO_IRQ_START_PATTERN_FILTERING                      0x000010
+#define ME_IO_IRQ_START_EXTENDED_STATUS                                0x000020
+
+/*==================================================================
+  Defines for meIOIrqWait function
+  ================================================================*/
+
+#define ME_IO_IRQ_WAIT_NO_FLAGS                                                0x000000
+#define ME_IO_IRQ_WAIT_NORMAL_STATUS                           0x000001
+#define ME_IO_IRQ_WAIT_EXTENDED_STATUS                         0x000002
+
+/*==================================================================
+  Defines for meIOIrqStop function
+  ================================================================*/
+
+#define ME_IO_IRQ_STOP_NO_FLAGS                                                0x000000
+
+/*==================================================================
+  Defines for meIOIrqSetCallback function
+  ================================================================*/
+
+#define ME_IO_IRQ_SET_CALLBACK_NO_FLAGS                                0x0
+
+/*==================================================================
+  Defines for meIOResetDevice function
+  ================================================================*/
+
+#define ME_IO_RESET_DEVICE_NO_FLAGS                                    0x0
+
+/*==================================================================
+  Defines for meIOResetSubdevice function
+  ================================================================*/
+
+#define ME_IO_RESET_SUBDEVICE_NO_FLAGS                         0x0
+
+/*==================================================================
+  Defines for meIOSingleConfig function
+  ================================================================*/
+
+#define ME_SINGLE_CONFIG_DIO_INPUT                                     0x000D0001
+#define ME_SINGLE_CONFIG_DIO_OUTPUT                                    0x000D0002
+#define ME_SINGLE_CONFIG_DIO_HIGH_IMPEDANCE                    0x000D0003
+#define ME_SINGLE_CONFIG_DIO_SINK                                      0x000D0004
+#define ME_SINGLE_CONFIG_DIO_SOURCE                                    0x000D0005
+#define ME_SINGLE_CONFIG_DIO_MUX32M                                    0x000D0006
+#define ME_SINGLE_CONFIG_DIO_DEMUX32                           0x000D0007
+#define ME_SINGLE_CONFIG_DIO_BIT_PATTERN                       0x000D0008
+
+#define ME_SINGLE_CONFIG_CTR_8254_MODE_0                       0x000E0001
+#define ME_SINGLE_CONFIG_CTR_8254_MODE_1                       0x000E0002
+#define ME_SINGLE_CONFIG_CTR_8254_MODE_2                       0x000E0003
+#define ME_SINGLE_CONFIG_CTR_8254_MODE_3                       0x000E0004
+#define ME_SINGLE_CONFIG_CTR_8254_MODE_4                       0x000E0005
+#define ME_SINGLE_CONFIG_CTR_8254_MODE_5                       0x000E0006
+
+#define ME_IO_SINGLE_CONFIG_NO_FLAGS                           0x00
+#define ME_IO_SINGLE_CONFIG_DIO_BIT                                    0x01
+#define ME_IO_SINGLE_CONFIG_DIO_BYTE                           0x02
+#define ME_IO_SINGLE_CONFIG_DIO_WORD                           0x04
+#define ME_IO_SINGLE_CONFIG_DIO_DWORD                          0x08
+#define ME_IO_SINGLE_CONFIG_MULTISIG_LED_ON                    0x10
+#define ME_IO_SINGLE_CONFIG_MULTISIG_LED_OFF           0x20
+#define ME_IO_SINGLE_CONFIG_AI_RMS                                     0x40
+#define ME_IO_SINGLE_CONFIG_CONTINUE                           0x80
+
+/*==================================================================
+  Defines for meIOSingle function
+  ================================================================*/
+
+#define ME_IO_SINGLE_NO_FLAGS                                          0x0
+#define ME_IO_SINGLE_NONBLOCKING                                       0x20
+
+#define ME_DIR_INPUT                                                           0x000F0001
+#define ME_DIR_OUTPUT                                                          0x000F0002
+
+#define ME_IO_SINGLE_TYPE_NO_FLAGS                                     0x00
+#define ME_IO_SINGLE_TYPE_DIO_BIT                                      0x01
+#define ME_IO_SINGLE_TYPE_DIO_BYTE                                     0x02
+#define ME_IO_SINGLE_TYPE_DIO_WORD                                     0x04
+#define ME_IO_SINGLE_TYPE_DIO_DWORD                                    0x08
+#define ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS                     0x10
+#define ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING                    0x20
+
+/*==================================================================
+  Defines for meIOStreamConfig function
+  ================================================================*/
+
+#define ME_IO_STREAM_CONFIG_NO_FLAGS                           0x0
+#define ME_IO_STREAM_CONFIG_BIT_PATTERN                                0x1
+#define ME_IO_STREAM_CONFIG_WRAPAROUND                         0x2
+#define ME_IO_STREAM_CONFIG_SAMPLE_AND_HOLD                    0x4
+#define ME_IO_STREAM_CONFIG_HARDWARE_ONLY                      0x8
+
+#define ME_IO_STREAM_CONFIG_TYPE_NO_FLAGS                      0x0
+
+#define ME_IO_STREAM_TRIGGER_TYPE_NO_FLAGS                     0x0
+
+/*==================================================================
+  Defines for meIOStreamRead function
+  ================================================================*/
+
+#define ME_READ_MODE_BLOCKING                                          0x00100001
+#define ME_READ_MODE_NONBLOCKING                                       0x00100002
+
+#define ME_IO_STREAM_READ_NO_FLAGS                                     0x0
+#define ME_IO_STREAM_READ_FRAMES                                       0x1
+
+/*==================================================================
+  Defines for meIOStreamWrite function
+  ================================================================*/
+
+#define ME_WRITE_MODE_BLOCKING                                         0x00110001
+#define ME_WRITE_MODE_NONBLOCKING                                      0x00110002
+#define ME_WRITE_MODE_PRELOAD                                          0x00110003
+
+#define ME_IO_STREAM_WRITE_NO_FLAGS                                    0x00000000
+
+/*==================================================================
+  Defines for meIOStreamStart function
+  ================================================================*/
+
+#define ME_IO_STREAM_START_NO_FLAGS                                    0x00000000
+
+#define ME_START_MODE_BLOCKING                                         0x00120001
+#define ME_START_MODE_NONBLOCKING                                      0x00120002
+
+#define ME_IO_STREAM_START_TYPE_NO_FLAGS                       0x0
+#define ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS       0x1
+
+/*==================================================================
+  Defines for meIOStreamStop function
+  ================================================================*/
+
+#define ME_IO_STREAM_STOP_NO_FLAGS                                     0x00000000
+#define ME_IO_STREAM_STOP_PRESERVE_BUFFERS                     0x00000001
+
+#define ME_STOP_MODE_IMMEDIATE                                         0x00130001
+#define ME_STOP_MODE_LAST_VALUE                                                0x00130002
+
+#define ME_IO_STREAM_STOP_TYPE_NO_FLAGS                                0x00000000
+
+/*==================================================================
+  Defines for meIOStreamStatus function
+  ================================================================*/
+
+#define ME_WAIT_NONE                                                           0x00140001
+#define ME_WAIT_IDLE                                                           0x00140002
+
+#define ME_STATUS_INVALID                                                      0x00000000
+#define ME_STATUS_IDLE                                                         0x00150001
+#define ME_STATUS_BUSY                                                         0x00150002
+#define ME_STATUS_ERROR                                                                0x00150003
+
+#define ME_IO_STREAM_STATUS_NO_FLAGS                           0x00000000
+
+/*==================================================================
+  Defines for meIOStreamSetCallbacks function
+  ================================================================*/
+
+#define ME_IO_STREAM_SET_CALLBACKS_NO_FLAGS                    0x00000000
+
+/*==================================================================
+  Defines for meIOStreamNewValues function
+  ================================================================*/
+
+#define ME_IO_STREAM_NEW_VALUES_NO_FLAGS                       0x00000000
+
+/*==================================================================
+  Defines for meIOTimeToTicks function
+  ================================================================*/
+
+#define ME_IO_STREAM_TIME_TO_TICKS_NO_FLAGS                    0x00000000
+
+/*==================================================================
+  Defines for module types
+  ================================================================*/
+
+#define ME_MODULE_TYPE_MULTISIG_NONE                           0x00000000
+#define ME_MODULE_TYPE_MULTISIG_DIFF16_10V                     0x00160001
+#define ME_MODULE_TYPE_MULTISIG_DIFF16_20V                     0x00160002
+#define ME_MODULE_TYPE_MULTISIG_DIFF16_50V                     0x00160003
+#define ME_MODULE_TYPE_MULTISIG_CURRENT16_0_20MA       0x00160004
+#define ME_MODULE_TYPE_MULTISIG_RTD8_PT100                     0x00160005
+#define ME_MODULE_TYPE_MULTISIG_RTD8_PT500                     0x00160006
+#define ME_MODULE_TYPE_MULTISIG_RTD8_PT1000                    0x00160007
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_B                     0x00160008
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_E                     0x00160009
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_J                     0x0016000A
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_K                     0x0016000B
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_N                     0x0016000C
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_R                     0x0016000D
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_S                     0x0016000E
+#define ME_MODULE_TYPE_MULTISIG_TE8_TYPE_T                     0x0016000F
+#define ME_MODULE_TYPE_MULTISIG_TE8_TEMP_SENSOR                0x00160010
+
+/*==================================================================
+  Defines for meQuerySubdeviceCaps function
+  ================================================================*/
+
+#define ME_CAPS_NONE                                                           0x00000000
+
+#define ME_CAPS_DIO_DIR_BIT                                                    0x00000001
+#define ME_CAPS_DIO_DIR_BYTE                                           0x00000002
+#define ME_CAPS_DIO_DIR_WORD                                           0x00000004
+#define ME_CAPS_DIO_DIR_DWORD                                          0x00000008
+#define ME_CAPS_DIO_SINK_SOURCE                                                0x00000010
+#define ME_CAPS_DIO_BIT_PATTERN_IRQ                                    0x00000020
+#define ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_RISING           0x00000040
+#define ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_FALLING          0x00000080
+#define ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_ANY                      0x00000100
+#define ME_CAPS_DIO_OVER_TEMP_IRQ                                      0x00000200
+
+#define ME_CAPS_CTR_CLK_PREVIOUS                                       0x00000001
+#define ME_CAPS_CTR_CLK_INTERNAL_1MHZ                          0x00000002
+#define ME_CAPS_CTR_CLK_INTERNAL_10MHZ                         0x00000004
+#define ME_CAPS_CTR_CLK_EXTERNAL                                       0x00000008
+
+#define ME_CAPS_AI_TRIG_SYNCHRONOUS                                    0x00000001
+/// @note Backward compatibility for me1600 in old style.
+#define ME_CAPS_AI_TRIG_SIMULTANEOUS                           ME_CAPS_AI_TRIG_SYNCHRONOUS
+#define ME_CAPS_AI_FIFO                                                                0x00000002
+#define ME_CAPS_AI_FIFO_THRESHOLD                                      0x00000004
+
+#define ME_CAPS_AO_TRIG_SYNCHRONOUS                                    0x00000001
+/// @note Backward compatibility for me1600 in old style.
+#define ME_CAPS_AO_TRIG_SIMULTANEOUS                           ME_CAPS_AO_TRIG_SYNCHRONOUS
+#define ME_CAPS_AO_FIFO                                                                0x00000002
+#define ME_CAPS_AO_FIFO_THRESHOLD                                      0x00000004
+
+#define ME_CAPS_EXT_IRQ_EDGE_RISING                                    0x00000001
+#define ME_CAPS_EXT_IRQ_EDGE_FALLING                           0x00000002
+#define ME_CAPS_EXT_IRQ_EDGE_ANY                                       0x00000004
+
+/*==================================================================
+  Defines for meQuerySubdeviceCapsArgs function
+  ================================================================*/
+
+#define ME_CAP_AI_FIFO_SIZE                                                    0x001D0000
+#define ME_CAP_AI_BUFFER_SIZE                                          0x001D0001
+
+#define ME_CAP_AO_FIFO_SIZE                                                    0x001F0000
+#define ME_CAP_AO_BUFFER_SIZE                                          0x001F0001
+
+#define ME_CAP_CTR_WIDTH                                                       0x00200000
+
+/*==================================================================
+  Defines common to query functions
+  ================================================================*/
+
+#define ME_UNIT_INVALID                                                                0x00000000
+#define ME_UNIT_VOLT                                                           0x00170001
+#define ME_UNIT_AMPERE                                                         0x00170002
+#define ME_UNIT_ANY                                                                    0x00170003
+
+#define ME_TYPE_INVALID                                                                0x00000000
+#define ME_TYPE_AO                                                                     0x00180001
+#define ME_TYPE_AI                                                                     0x00180002
+#define ME_TYPE_DIO                                                                    0x00180003
+#define ME_TYPE_DO                                                                     0x00180004
+#define ME_TYPE_DI                                                                     0x00180005
+#define ME_TYPE_CTR                                                                    0x00180006
+#define ME_TYPE_EXT_IRQ                                                                0x00180007
+
+#define ME_SUBTYPE_INVALID                                                     0x00000000
+#define ME_SUBTYPE_SINGLE                                                      0x00190001
+#define ME_SUBTYPE_STREAMING                                           0x00190002
+#define ME_SUBTYPE_CTR_8254                                                    0x00190003
+#define ME_SUBTYPE_ANY                                                         0x00190004
+
+#define ME_DEVICE_DRIVER_NAME_MAX_COUNT                                64
+#define ME_DEVICE_NAME_MAX_COUNT                                       64
+
+#define ME_DEVICE_DESCRIPTION_MAX_COUNT                                256
+
+#define ME_BUS_TYPE_INVALID                                                    0x00000000
+#define ME_BUS_TYPE_PCI                                                                0x001A0001
+#define ME_BUS_TYPE_USB                                                                0x001A0002
+
+#define ME_PLUGGED_INVALID                                                     0x00000000
+#define ME_PLUGGED_IN                                                          0x001B0001
+#define ME_PLUGGED_OUT                                                         0x001B0002
+
+#define ME_EXTENSION_TYPE_INVALID                                      0x00000000
+#define ME_EXTENSION_TYPE_NONE                                         0x001C0001
+#define ME_EXTENSION_TYPE_MUX32M                                       0x001C0002
+#define ME_EXTENSION_TYPE_DEMUX32                                      0x001C0003
+#define ME_EXTENSION_TYPE_MUX32S                                       0x001C0004
+
+#define ME_ACCESS_TYPE_INVALID                                         0x00000000
+#define ME_ACCESS_TYPE_LOCAL                                           0x001D0001
+#define ME_ACCESS_TYPE_REMOTE                                          0x001D0002
+
+/// @note Add by KG
+
+/*==================================================================
+  Defines for meUtilityPWM
+  ================================================================*/
+#define ME_PWM_START_CONNECT_INTERNAL                          0x00200001
+
+/* Flags for SingleConfig channels configure */
+#define ME_SINGLE_CHANNEL_NOT_CONFIGURED                       0x00
+#define ME_SINGLE_CHANNEL_CONFIGURED                           0x01
+
+/* Define if configuration should be downloaded to driver */
+#define ME_CONFIG_LOAD_NO_FLAGS                                                0x0
+#define ME_CONFIG_LOAD_TO_DRIVER                                       0x1
+
+#endif
diff --git a/drivers/staging/meilhaus/medevice.c b/drivers/staging/meilhaus/medevice.c
new file mode 100644 (file)
index 0000000..8f62e16
--- /dev/null
@@ -0,0 +1,1740 @@
+/**
+ * @file medevice.c
+ *
+ * @brief Meilhaus device base class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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.
+ */
+
+#include "mecommon.h"
+#include "meinternal.h"
+#include "medefines.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "medevice.h"
+
+#ifndef __KERNEL__
+#  define __KERNEL__
+#endif
+
+static int me_device_io_irq_start(struct me_device *device,
+                                 struct file *filep,
+                                 int subdevice,
+                                 int channel,
+                                 int irq_source,
+                                 int irq_edge, int irq_arg, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_irq_start(s,
+                                                  filep,
+                                                  channel,
+                                                  irq_source,
+                                                  irq_edge, irq_arg, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_irq_wait(struct me_device *device,
+                                struct file *filep,
+                                int subdevice,
+                                int channel,
+                                int *irq_count,
+                                int *value, int time_out, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_irq_wait(s,
+                                                 filep,
+                                                 channel,
+                                                 irq_count,
+                                                 value, time_out, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_irq_stop(struct me_device *device,
+                                struct file *filep,
+                                int subdevice, int channel, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_irq_stop(s, filep, channel, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_reset_device(struct me_device *device,
+                                    struct file *filep, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+       int i, n;
+
+       PDEBUG("executed.\n");
+
+       /* Get the number of subdevices. */
+       n = me_slist_get_number_subdevices(&device->slist);
+
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+
+       /* Reset every subdevice in list. */
+       for (i = 0; i < n; i++) {
+               s = me_slist_get_subdevice(&device->slist, i);
+               err = s->me_subdevice_io_reset_subdevice(s, filep, flags);
+
+               if (err) {
+                       PERROR("Cannot reset subdevice.\n");
+                       break;
+               }
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_reset_subdevice(struct me_device *device,
+                                       struct file *filep,
+                                       int subdevice, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_reset_subdevice(s, filep, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_single_config(struct me_device *device,
+                                     struct file *filep,
+                                     int subdevice,
+                                     int channel,
+                                     int single_config,
+                                     int ref,
+                                     int trig_chan,
+                                     int trig_type, int trig_edge, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_single_config(s,
+                                                      filep,
+                                                      channel,
+                                                      single_config,
+                                                      ref,
+                                                      trig_chan,
+                                                      trig_type,
+                                                      trig_edge, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_single_read(struct me_device *device,
+                                   struct file *filep,
+                                   int subdevice,
+                                   int channel,
+                                   int *value, int time_out, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_single_read(s,
+                                                    filep,
+                                                    channel,
+                                                    value, time_out, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_single_write(struct me_device *device,
+                                    struct file *filep,
+                                    int subdevice,
+                                    int channel,
+                                    int value, int time_out, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_single_write(s,
+                                                     filep,
+                                                     channel,
+                                                     value, time_out, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_config(struct me_device *device,
+                                     struct file *filep,
+                                     int subdevice,
+                                     meIOStreamConfig_t * config_list,
+                                     int count,
+                                     meIOStreamTrigger_t * trigger,
+                                     int fifo_irq_threshold, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_config(s,
+                                                      filep,
+                                                      config_list,
+                                                      count,
+                                                      trigger,
+                                                      fifo_irq_threshold,
+                                                      flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_new_values(struct me_device *device,
+                                         struct file *filep,
+                                         int subdevice,
+                                         int time_out, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_new_values(s,
+                                                          filep,
+                                                          time_out,
+                                                          count, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_read(struct me_device *device,
+                                   struct file *filep,
+                                   int subdevice,
+                                   int read_mode,
+                                   int *values, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_read(s,
+                                                    filep,
+                                                    read_mode,
+                                                    values, count, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_start(struct me_device *device,
+                                    struct file *filep,
+                                    int subdevice,
+                                    int start_mode, int time_out, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_start(s,
+                                                     filep,
+                                                     start_mode,
+                                                     time_out, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_status(struct me_device *device,
+                                     struct file *filep,
+                                     int subdevice,
+                                     int wait,
+                                     int *status, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_status(s,
+                                                      filep,
+                                                      wait,
+                                                      status, count, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_stop(struct me_device *device,
+                                   struct file *filep,
+                                   int subdevice, int stop_mode, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_stop(s,
+                                                    filep, stop_mode, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_io_stream_write(struct me_device *device,
+                                    struct file *filep,
+                                    int subdevice,
+                                    int write_mode,
+                                    int *values, int *count, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_io_stream_write(s,
+                                                     filep,
+                                                     write_mode,
+                                                     values, count, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_lock_device(struct me_device *device,
+                                struct file *filep, int lock, int flags)
+{
+       PDEBUG("executed.\n");
+
+       return me_dlock_lock(&device->dlock,
+                            filep, lock, flags, &device->slist);
+}
+
+static int me_device_lock_subdevice(struct me_device *device,
+                                   struct file *filep,
+                                   int subdevice, int lock, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Enter device.
+       err = me_dlock_enter(&device->dlock, filep);
+
+       if (err) {
+               PERROR("Cannot enter device.\n");
+               return err;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_lock_subdevice(s, filep, lock, flags);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       // Exit device.
+       me_dlock_exit(&device->dlock, filep);
+
+       return err;
+}
+
+static int me_device_query_description_device(struct me_device *device,
+                                             char **description)
+{
+       PDEBUG("executed.\n");
+       *description = device->device_description;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_device_query_info_device(struct me_device *device,
+                                      int *vendor_id,
+                                      int *device_id,
+                                      int *serial_no,
+                                      int *bus_type,
+                                      int *bus_no,
+                                      int *dev_no, int *func_no, int *plugged)
+{
+       PDEBUG("executed.\n");
+
+       if (device->bus_type == ME_BUS_TYPE_PCI) {
+               *vendor_id = device->info.pci.vendor_id;
+               *device_id = device->info.pci.device_id;
+               *serial_no = device->info.pci.serial_no;
+               *bus_type = ME_BUS_TYPE_PCI;
+               *bus_no = device->info.pci.pci_bus_no;
+               *dev_no = device->info.pci.pci_dev_no;
+               *func_no = device->info.pci.pci_func_no;
+               *plugged = ME_PLUGGED_IN;
+       } else {
+               *plugged = ME_PLUGGED_OUT;
+       }
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_device_query_name_device(struct me_device *device, char **name)
+{
+       PDEBUG("executed.\n");
+       *name = device->device_name;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_device_query_name_device_driver(struct me_device *device,
+                                             char **name)
+{
+       PDEBUG("executed.\n");
+       *name = device->driver_name;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_device_query_number_subdevices(struct me_device *device,
+                                            int *number)
+{
+       PDEBUG("executed.\n");
+       return me_slist_query_number_subdevices(&device->slist, number);
+}
+
+static int me_device_query_number_channels(struct me_device *device,
+                                          int subdevice, int *number)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_number_channels(s, number);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_number_ranges(struct me_device *device,
+                                        int subdevice, int unit, int *count)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_number_ranges(s, unit, count);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_range_by_min_max(struct me_device *device,
+                                           int subdevice,
+                                           int unit,
+                                           int *min,
+                                           int *max, int *maxdata, int *range)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_range_by_min_max(s,
+                                                            unit,
+                                                            min,
+                                                            max,
+                                                            maxdata, range);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_range_info(struct me_device *device,
+                                     int subdevice,
+                                     int range,
+                                     int *unit,
+                                     int *min, int *max, int *maxdata)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_range_info(s,
+                                                      range,
+                                                      unit, min, max, maxdata);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_subdevice_by_type(struct me_device *device,
+                                            int start_subdevice,
+                                            int type,
+                                            int subtype, int *subdevice)
+{
+       PDEBUG("executed.\n");
+
+       return me_slist_get_subdevice_by_type(&device->slist,
+                                             start_subdevice,
+                                             type, subtype, subdevice);
+}
+
+static int me_device_query_subdevice_type(struct me_device *device,
+                                         int subdevice,
+                                         int *type, int *subtype)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_subdevice_type(s, type, subtype);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_subdevice_caps(struct me_device *device,
+                                         int subdevice, int *caps)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_subdevice_caps(s, caps);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_subdevice_caps_args(struct me_device *device,
+                                              int subdevice,
+                                              int cap, int *args, int count)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_subdevice_caps_args(s,
+                                                               cap,
+                                                               args, count);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_timer(struct me_device *device,
+                                int subdevice,
+                                int timer,
+                                int *base_frequency,
+                                uint64_t * min_ticks, uint64_t * max_ticks)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_subdevice_t *s;
+
+       PDEBUG("executed.\n");
+
+       // Check subdevice index.
+
+       if (subdevice >= me_slist_get_number_subdevices(&device->slist)) {
+               PERROR("Invalid subdevice.\n");
+               return ME_ERRNO_INVALID_SUBDEVICE;
+       }
+       // Get subdevice instance.
+       s = me_slist_get_subdevice(&device->slist, subdevice);
+
+       if (s) {
+               // Call subdevice method.
+               err = s->me_subdevice_query_timer(s,
+                                                 timer,
+                                                 base_frequency,
+                                                 min_ticks, max_ticks);
+       } else {
+               // Something really bad happened.
+               PERROR("Cannot get subdevice instance.\n");
+               err = ME_ERRNO_INTERNAL;
+       }
+
+       return err;
+}
+
+static int me_device_query_version_device_driver(struct me_device *device,
+                                                int *version)
+/** @todo Versions shold be read from driver. I must overwrite this function in each module. Here should be returned an error!
+*/
+{
+       PDEBUG("executed.\n");
+       *version = ME_VERSION_DRIVER;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_device_config_load(struct me_device *device, struct file *filep,
+                                me_cfg_device_entry_t * config)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_SUCCESS;        //If no need for config return success.
+//      return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static void me_device_destructor(me_device_t * me_device)
+{
+       PDEBUG("executed.\n");
+       me_device_deinit(me_device);
+       kfree(me_device);
+}
+
+/* //me_device_usb_init
+int me_device_usb_init(me_device_t *me_device, struct usb_interface *interface)
+{
+       PDEBUG("executed.\n");
+       return -1;
+}
+*/
+
+static int get_device_descriptions(uint16_t device_id,
+                                  char **device_name,
+                                  char **device_description,
+                                  char **driver_name)
+/** @todo This is wrong concept! Static table has too strong limitations!
+* 'device_name' and 'driver_name' should be calculated from 'device_id'
+* 'device_description' should be read from device or moved to user space and handled by library!
+*/
+{
+       PDEBUG("executed.\n");
+
+       switch (device_id) {
+       case PCI_DEVICE_ID_MEILHAUS_ME1000:
+       case PCI_DEVICE_ID_MEILHAUS_ME1000_A:
+       case PCI_DEVICE_ID_MEILHAUS_ME1000_B:
+               *device_name = ME1000_NAME_DEVICE_ME1000;
+               *device_description = ME1000_DESCRIPTION_DEVICE_ME1000;
+               *driver_name = ME1000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+               *device_name = ME1400_NAME_DEVICE_ME1400;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+               *device_name = ME1400_NAME_DEVICE_ME1400A;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400A;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:
+               *device_name = ME1400_NAME_DEVICE_ME1400B;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400B;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+               *device_name = ME1400_NAME_DEVICE_ME1400E;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400E;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+               *device_name = ME1400_NAME_DEVICE_ME1400EA;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400EA;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               *device_name = ME1400_NAME_DEVICE_ME1400EB;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400EB;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+               *device_name = ME1400_NAME_DEVICE_ME1400C;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400C;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               *device_name = ME1400_NAME_DEVICE_ME1400D;
+               *device_description = ME1400_DESCRIPTION_DEVICE_ME1400D;
+               *driver_name = ME1400_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_4U:
+               *device_name = ME1600_NAME_DEVICE_ME16004U;
+               *device_description = ME1600_DESCRIPTION_DEVICE_ME16004U;
+               *driver_name = ME1600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_8U:
+               *device_name = ME1600_NAME_DEVICE_ME16008U;
+               *device_description = ME1600_DESCRIPTION_DEVICE_ME16008U;
+               *driver_name = ME1600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_12U:
+               *device_name = ME1600_NAME_DEVICE_ME160012U;
+               *device_description = ME1600_DESCRIPTION_DEVICE_ME160012U;
+               *driver_name = ME1600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_16U:
+               *device_name = ME1600_NAME_DEVICE_ME160016U;
+               *device_description = ME1600_DESCRIPTION_DEVICE_ME160016U;
+               *driver_name = ME1600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I:
+               *device_name = ME1600_NAME_DEVICE_ME160016U8I;
+               *device_description = ME1600_DESCRIPTION_DEVICE_ME160016U8I;
+               *driver_name = ME1600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4610:
+               *device_name = ME4600_NAME_DEVICE_ME4610;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4610;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4650:
+               *device_name = ME4600_NAME_DEVICE_ME4650;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4650;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660:
+               *device_name = ME4600_NAME_DEVICE_ME4660;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4660;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660I:
+               *device_name = ME4600_NAME_DEVICE_ME4660I;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4660I;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660S:
+               *device_name = ME4600_NAME_DEVICE_ME4660S;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4660S;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660IS:
+               *device_name = ME4600_NAME_DEVICE_ME4660IS;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4660IS;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670:
+               *device_name = ME4600_NAME_DEVICE_ME4670;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4670;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670I:
+               *device_name = ME4600_NAME_DEVICE_ME4670I;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4670I;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670S:
+               *device_name = ME4600_NAME_DEVICE_ME4670S;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4670S;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670IS:
+               *device_name = ME4600_NAME_DEVICE_ME4670IS;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4670IS;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680:
+               *device_name = ME4600_NAME_DEVICE_ME4680;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4680;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680I:
+               *device_name = ME4600_NAME_DEVICE_ME4680I;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4680I;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680S:
+               *device_name = ME4600_NAME_DEVICE_ME4680S;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4680S;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680IS:
+               *device_name = ME4600_NAME_DEVICE_ME4680IS;
+               *device_description = ME4600_DESCRIPTION_DEVICE_ME4680IS;
+               *driver_name = ME4600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6004:
+               *device_name = ME6000_NAME_DEVICE_ME60004;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME60004;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6008:
+               *device_name = ME6000_NAME_DEVICE_ME60008;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME60008;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME600F:
+               *device_name = ME6000_NAME_DEVICE_ME600016;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME600016;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6014:
+               *device_name = ME6000_NAME_DEVICE_ME6000I4;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000I4;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6018:
+               *device_name = ME6000_NAME_DEVICE_ME6000I8;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000I8;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME601F:
+               *device_name = ME6000_NAME_DEVICE_ME6000I16;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000I16;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6034:
+               *device_name = ME6000_NAME_DEVICE_ME6000ISLE4;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE4;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6038:
+               *device_name = ME6000_NAME_DEVICE_ME6000ISLE8;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE8;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME603F:
+               *device_name = ME6000_NAME_DEVICE_ME6000ISLE16;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE16;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6104:
+               *device_name = ME6000_NAME_DEVICE_ME61004;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME61004;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6108:
+               *device_name = ME6000_NAME_DEVICE_ME61008;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME61008;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME610F:
+               *device_name = ME6000_NAME_DEVICE_ME610016;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME610016;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6114:
+               *device_name = ME6000_NAME_DEVICE_ME6100I4;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100I4;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6118:
+               *device_name = ME6000_NAME_DEVICE_ME6100I8;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100I8;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME611F:
+               *device_name = ME6000_NAME_DEVICE_ME6100I16;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100I16;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6134:
+               *device_name = ME6000_NAME_DEVICE_ME6100ISLE4;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE4;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6138:
+               *device_name = ME6000_NAME_DEVICE_ME6100ISLE8;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE8;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME613F:
+               *device_name = ME6000_NAME_DEVICE_ME6100ISLE16;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE16;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6044:
+               *device_name = ME6000_NAME_DEVICE_ME60004DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME60004DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6048:
+               *device_name = ME6000_NAME_DEVICE_ME60008DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME60008DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME604F:
+               *device_name = ME6000_NAME_DEVICE_ME600016DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME600016DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6054:
+               *device_name = ME6000_NAME_DEVICE_ME6000I4DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000I4DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6058:
+               *device_name = ME6000_NAME_DEVICE_ME6000I8DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000I8DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME605F:
+               *device_name = ME6000_NAME_DEVICE_ME6000I16DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000I16DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6074:
+               *device_name = ME6000_NAME_DEVICE_ME6000ISLE4DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE4DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6078:
+               *device_name = ME6000_NAME_DEVICE_ME6000ISLE8DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE8DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME607F:
+               *device_name = ME6000_NAME_DEVICE_ME6000ISLE16DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE16DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6144:
+               *device_name = ME6000_NAME_DEVICE_ME61004DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME61004DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6148:
+               *device_name = ME6000_NAME_DEVICE_ME61008DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME61008DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME614F:
+               *device_name = ME6000_NAME_DEVICE_ME610016DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME610016DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6154:
+               *device_name = ME6000_NAME_DEVICE_ME6100I4DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100I4DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6158:
+               *device_name = ME6000_NAME_DEVICE_ME6100I8DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100I8DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME615F:
+               *device_name = ME6000_NAME_DEVICE_ME6100I16DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100I16DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6174:
+               *device_name = ME6000_NAME_DEVICE_ME6100ISLE4DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE4DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6178:
+               *device_name = ME6000_NAME_DEVICE_ME6100ISLE8DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE8DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME617F:
+               *device_name = ME6000_NAME_DEVICE_ME6100ISLE16DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE16DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6259:
+               *device_name = ME6000_NAME_DEVICE_ME6200I9DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6200I9DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6359:
+               *device_name = ME6000_NAME_DEVICE_ME6300I9DIO;
+               *device_description = ME6000_DESCRIPTION_DEVICE_ME6300I9DIO;
+               *driver_name = ME6000_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0630:
+               *device_name = ME0600_NAME_DEVICE_ME0630;
+               *device_description = ME0600_DESCRIPTION_DEVICE_ME0630;
+               *driver_name = ME0600_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_A:
+               *device_name = ME8100_NAME_DEVICE_ME8100A;
+               *device_description = ME8100_DESCRIPTION_DEVICE_ME8100A;
+               *driver_name = ME8100_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_B:
+               *device_name = ME8100_NAME_DEVICE_ME8100B;
+               *device_description = ME8100_DESCRIPTION_DEVICE_ME8100B;
+               *driver_name = ME8100_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8200_A:
+               *device_name = ME8200_NAME_DEVICE_ME8200A;
+               *device_description = ME8200_DESCRIPTION_DEVICE_ME8200A;
+               *driver_name = ME8200_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8200_B:
+               *device_name = ME8200_NAME_DEVICE_ME8200B;
+               *device_description = ME8200_DESCRIPTION_DEVICE_ME8200B;
+               *driver_name = ME8200_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0940:
+               *device_name = ME0900_NAME_DEVICE_ME0940;
+               *device_description = ME0900_DESCRIPTION_DEVICE_ME0940;
+               *driver_name = ME0900_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0950:
+               *device_name = ME0900_NAME_DEVICE_ME0950;
+               *device_description = ME0900_DESCRIPTION_DEVICE_ME0950;
+               *driver_name = ME0900_NAME_DRIVER;
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0960:
+               *device_name = ME0900_NAME_DEVICE_ME0960;
+               *device_description = ME0900_DESCRIPTION_DEVICE_ME0960;
+               *driver_name = ME0900_NAME_DRIVER;
+               break;
+/*
+               case USB_DEVICE_ID_MEPHISTO_S1:
+                       *device_name = MEPHISTO_S1_NAME_DEVICE;
+                       *device_description = MEPHISTO_S1_DESCRIPTION_DEVICE;
+                       *driver_name = MEPHISTO_S1_NAME_DRIVER;
+                       break;
+*/
+       default:
+               *device_name = EMPTY_NAME_DEVICE;
+               *device_description = EMPTY_DESCRIPTION_DEVICE;
+               *driver_name = EMPTY_NAME_DRIVER;
+
+               PERROR("Invalid device id.\n");
+
+               return 1;
+       }
+
+       return 0;
+}
+
+int me_device_pci_init(me_device_t * me_device, struct pci_dev *pci_device)
+{
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Initialize device list head.
+       INIT_LIST_HEAD(&me_device->list);
+
+       // Initialize device description strings.
+       err = get_device_descriptions(pci_device->device,
+                                     &me_device->device_name,
+                                     &me_device->device_description,
+                                     &me_device->driver_name);
+
+       if (err) {
+               PERROR("Cannot initialize device description strings.\n");
+               return 1;
+       }
+       // Enable the pci device.
+       err = pci_enable_device(pci_device);
+
+       if (err < 0) {
+               PERROR("Cannot enable PCI device.\n");
+               return 1;
+       }
+       // Request the PCI register regions.
+       err = pci_request_regions(pci_device, me_device->device_name);
+
+       if (err < 0) {
+               PERROR("Cannot request PCI regions.\n");
+               goto ERROR_0;
+       }
+       // The bus carrying the device is a PCI bus.
+       me_device->bus_type = ME_BUS_TYPE_PCI;
+
+       // Store the PCI information for later usage.
+       me_device->info.pci.pci_device = pci_device;
+
+       // Get PCI register bases and sizes.
+       for (i = 0; i < 6; i++) {
+               me_device->info.pci.reg_bases[i] =
+                   pci_resource_start(pci_device, i);
+               me_device->info.pci.reg_sizes[i] =
+                   pci_resource_len(pci_device, i);
+       }
+
+       // Get the PCI location.
+       me_device->info.pci.pci_bus_no = pci_device->bus->number;
+       me_device->info.pci.pci_dev_no = PCI_SLOT(pci_device->devfn);
+       me_device->info.pci.pci_func_no = PCI_FUNC(pci_device->devfn);
+
+       // Get Meilhaus specific device information.
+       me_device->info.pci.vendor_id = pci_device->vendor;
+       me_device->info.pci.device_id = pci_device->device;
+       pci_read_config_byte(pci_device, 0x08,
+                            &me_device->info.pci.hw_revision);
+       pci_read_config_dword(pci_device, 0x2C, &me_device->info.pci.serial_no);
+
+       // Get the interrupt request number.
+       me_device->irq = pci_device->irq;
+
+       // Initialize device lock instance.
+       err = me_dlock_init(&me_device->dlock);
+
+       if (err) {
+               PERROR("Cannot initialize device lock instance.\n");
+               goto ERROR_1;
+       }
+       // Initialize subdevice list instance.
+       me_slist_init(&me_device->slist);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice list instance.\n");
+               goto ERROR_2;
+       }
+       // Initialize method pointers.
+       me_device->me_device_io_irq_start = me_device_io_irq_start;
+       me_device->me_device_io_irq_wait = me_device_io_irq_wait;
+       me_device->me_device_io_irq_stop = me_device_io_irq_stop;
+       me_device->me_device_io_reset_device = me_device_io_reset_device;
+       me_device->me_device_io_reset_subdevice = me_device_io_reset_subdevice;
+       me_device->me_device_io_single_config = me_device_io_single_config;
+       me_device->me_device_io_single_read = me_device_io_single_read;
+       me_device->me_device_io_single_write = me_device_io_single_write;
+       me_device->me_device_io_stream_config = me_device_io_stream_config;
+       me_device->me_device_io_stream_new_values =
+           me_device_io_stream_new_values;
+       me_device->me_device_io_stream_read = me_device_io_stream_read;
+       me_device->me_device_io_stream_start = me_device_io_stream_start;
+       me_device->me_device_io_stream_status = me_device_io_stream_status;
+       me_device->me_device_io_stream_stop = me_device_io_stream_stop;
+       me_device->me_device_io_stream_write = me_device_io_stream_write;
+       me_device->me_device_lock_device = me_device_lock_device;
+       me_device->me_device_lock_subdevice = me_device_lock_subdevice;
+       me_device->me_device_query_description_device =
+           me_device_query_description_device;
+       me_device->me_device_query_info_device = me_device_query_info_device;
+       me_device->me_device_query_name_device = me_device_query_name_device;
+       me_device->me_device_query_name_device_driver =
+           me_device_query_name_device_driver;
+       me_device->me_device_query_number_subdevices =
+           me_device_query_number_subdevices;
+       me_device->me_device_query_number_channels =
+           me_device_query_number_channels;
+       me_device->me_device_query_number_ranges =
+           me_device_query_number_ranges;
+       me_device->me_device_query_range_by_min_max =
+           me_device_query_range_by_min_max;
+       me_device->me_device_query_range_info = me_device_query_range_info;
+       me_device->me_device_query_subdevice_by_type =
+           me_device_query_subdevice_by_type;
+       me_device->me_device_query_subdevice_type =
+           me_device_query_subdevice_type;
+       me_device->me_device_query_subdevice_caps =
+           me_device_query_subdevice_caps;
+       me_device->me_device_query_subdevice_caps_args =
+           me_device_query_subdevice_caps_args;
+       me_device->me_device_query_timer = me_device_query_timer;
+       me_device->me_device_query_version_device_driver =
+           me_device_query_version_device_driver;
+       me_device->me_device_config_load = me_device_config_load;
+       me_device->me_device_destructor = me_device_destructor;
+
+       return 0;
+
+      ERROR_0:
+       me_dlock_deinit(&me_device->dlock);
+
+      ERROR_1:
+       pci_release_regions(pci_device);
+
+      ERROR_2:
+       pci_disable_device(pci_device);
+
+       return 1;
+}
+
+void me_device_deinit(me_device_t * me_device)
+{
+       PDEBUG("executed.\n");
+
+       me_slist_deinit(&me_device->slist);
+       me_dlock_deinit(&me_device->dlock);
+
+       if (me_device->bus_type == ME_BUS_TYPE_PCI) {
+               pci_release_regions(me_device->info.pci.pci_device);
+               pci_disable_device(me_device->info.pci.pci_device);
+       }
+/*
+       else
+       {
+               // Must be an USB device.
+       }
+*/
+}
diff --git a/drivers/staging/meilhaus/medevice.h b/drivers/staging/meilhaus/medevice.h
new file mode 100644 (file)
index 0000000..25da828
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : medevice.h
+ * Author      : GG (Guenter Gebhardt)  <support@meilhaus.de>
+ */
+
+#ifndef _MEDEVICE_H_
+#define _MEDEVICE_H_
+
+#ifndef KBUILD_MODNAME
+#  define KBUILD_MODNAME KBUILD_STR(memain)
+#endif
+
+#include <linux/pci.h>
+//#include <linux/usb.h>
+#include <linux/fs.h>
+#include <linux/spinlock.h>
+
+#include "metypes.h"
+#include "meslist.h"
+#include "medlock.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Defines a pointer type to a PCI constructor function.
+ */
+typedef struct me_device *(*me_pci_constructor_t) (struct pci_dev *);
+
+/**
+ * @brief Defines a pointer type to a ME-4000 PCI constructor function.
+ */
+#ifdef BOSCH
+typedef struct me_device *(*me_bosch_constructor_t) (struct pci_dev *,
+                                                    int me_bosch_fw);
+#endif
+
+/**
+ * @brief Defines a pointer type to a USB constructor function.
+ */
+//typedef struct me_device *(*me_usb_constructor_t)(struct usb_interface *);
+
+/**
+ * @brief Defines a pointer type to the dummy constructor function.
+ */
+typedef struct me_device *(*me_dummy_constructor_t) (unsigned short vendor_id,
+                                                    unsigned short device_id,
+                                                    unsigned int serial_no,
+                                                    int bus_type,
+                                                    int bus_no,
+                                                    int dev_no, int func_no);
+
+//extern me_usb_constructor_t mephisto_s1_constructor __attribute__ ((weak));
+
+/**
+ * @brief Holds the PCI device information.
+ */
+typedef struct me_pci_info {
+       struct pci_dev *pci_device;                     /**< Kernel PCI device structure. */
+       uint32_t reg_bases[6];                          /**< The base adresses of the PCI bars. */
+       uint32_t reg_sizes[6];                          /**< The sizes of the PCI bars. */
+
+       uint32_t pci_bus_no;                            /**< PCI bus number. */
+       uint32_t pci_dev_no;                            /**< PCI device number. */
+       uint32_t pci_func_no;                           /**< PCI function number. */
+
+       uint16_t vendor_id;                                     /**< Meilhaus PCI vendor id. */
+       uint16_t device_id;                                     /**< Meilhaus device id. */
+       uint8_t hw_revision;                            /**< Hardware revision of the device. */
+       uint32_t serial_no;                                     /**< Serial number of the device. */
+} me_pci_info_t;
+
+/**
+ * @brief Holds the USB device information.
+ */
+//typedef struct me_usb_info {
+//} me_usb_info_t;
+
+/**
+ * @brief The Meilhaus device base class structure.
+ */
+typedef struct me_device {
+       /* Attributes */
+       struct list_head list;                          /**< Enables the device to be added to a dynamic list. */
+//      int magic;                                                      /**< The magic number of the structure. */
+
+       int bus_type;                                           /**< The descriminator for the union. */
+       union {
+               me_pci_info_t pci;                              /**< PCI specific device information. */
+//              me_usb_info_t usb;                              /**< USB specific device information. */
+       } info;                                                         /**< Holds the device information. */
+
+       int irq;                                                        /**< The irq assigned to this device. */
+
+       me_dlock_t dlock;                                       /**< The device locking structure. */
+       me_slist_t slist;                                       /**< The container holding all subdevices belonging to this device. */
+
+       char *device_name;                                      /**< The name of the Meilhaus device. */
+       char *device_description;                       /**< The description of the Meilhaus device. */
+       char *driver_name;                                      /**< The name of the device driver module supporting the device family. */
+
+       /* Methods */
+       int (*me_device_io_irq_start) (struct me_device * device,
+                                      struct file * filep,
+                                      int subdevice,
+                                      int channel,
+                                      int irq_source,
+                                      int irq_edge, int irq_arg, int flags);
+
+       int (*me_device_io_irq_wait) (struct me_device * device,
+                                     struct file * filep,
+                                     int subdevice,
+                                     int channel,
+                                     int *irq_count,
+                                     int *value, int time_out, int flags);
+
+       int (*me_device_io_irq_stop) (struct me_device * device,
+                                     struct file * filep,
+                                     int subdevice, int channel, int flags);
+
+       int (*me_device_io_reset_device) (struct me_device * device,
+                                         struct file * filep, int flags);
+
+       int (*me_device_io_reset_subdevice) (struct me_device * device,
+                                            struct file * filep,
+                                            int subdevice, int flags);
+
+       int (*me_device_io_single_config) (struct me_device * device,
+                                          struct file * filep,
+                                          int subdevice,
+                                          int channel,
+                                          int single_config,
+                                          int ref,
+                                          int trig_chan,
+                                          int trig_type,
+                                          int trig_edge, int flags);
+
+       int (*me_device_io_single_read) (struct me_device * device,
+                                        struct file * filep,
+                                        int subdevice,
+                                        int channel,
+                                        int *value, int time_out, int flags);
+
+       int (*me_device_io_single_write) (struct me_device * device,
+                                         struct file * filep,
+                                         int subdevice,
+                                         int channel,
+                                         int value, int time_out, int flags);
+
+       int (*me_device_io_stream_config) (struct me_device * device,
+                                          struct file * filep,
+                                          int subdevice,
+                                          meIOStreamConfig_t * config_list,
+                                          int count,
+                                          meIOStreamTrigger_t * trigger,
+                                          int fifo_irq_threshold, int flags);
+
+       int (*me_device_io_stream_new_values) (struct me_device * device,
+                                              struct file * filep,
+                                              int subdevice,
+                                              int time_out,
+                                              int *count, int flags);
+
+       int (*me_device_io_stream_read) (struct me_device * device,
+                                        struct file * filep,
+                                        int subdevice,
+                                        int read_mode,
+                                        int *values, int *count, int flags);
+
+       int (*me_device_io_stream_start) (struct me_device * device,
+                                         struct file * filep,
+                                         int subdevice,
+                                         int start_mode,
+                                         int time_out, int flags);
+
+       int (*me_device_io_stream_status) (struct me_device * device,
+                                          struct file * filep,
+                                          int subdevice,
+                                          int wait,
+                                          int *status, int *count, int flags);
+
+       int (*me_device_io_stream_stop) (struct me_device * device,
+                                        struct file * filep,
+                                        int subdevice,
+                                        int stop_mode, int flags);
+
+       int (*me_device_io_stream_write) (struct me_device * device,
+                                         struct file * filep,
+                                         int subdevice,
+                                         int write_mode,
+                                         int *values, int *count, int flags);
+
+       int (*me_device_lock_device) (struct me_device * device,
+                                     struct file * filep, int lock, int flags);
+
+       int (*me_device_lock_subdevice) (struct me_device * device,
+                                        struct file * filep,
+                                        int subdevice, int lock, int flags);
+
+       int (*me_device_query_description_device) (struct me_device * device,
+                                                  char **description);
+
+       int (*me_device_query_info_device) (struct me_device * device,
+                                           int *vendor_id,
+                                           int *device_id,
+                                           int *serial_no,
+                                           int *bus_type,
+                                           int *bus_no,
+                                           int *dev_no,
+                                           int *func_no, int *plugged);
+
+       int (*me_device_query_name_device) (struct me_device * device,
+                                           char **name);
+
+       int (*me_device_query_name_device_driver) (struct me_device * device,
+                                                  char **name);
+
+       int (*me_device_query_number_subdevices) (struct me_device * device,
+                                                 int *number);
+
+       int (*me_device_query_number_channels) (struct me_device * device,
+                                               int subdevice, int *number);
+
+       int (*me_device_query_number_ranges) (struct me_device * device,
+                                             int subdevice,
+                                             int unit, int *count);
+
+       int (*me_device_query_range_by_min_max) (struct me_device * device,
+                                                int subdevice,
+                                                int unit,
+                                                int *min,
+                                                int *max,
+                                                int *maxdata, int *range);
+
+       int (*me_device_query_range_info) (struct me_device * device,
+                                          int subdevice,
+                                          int range,
+                                          int *unit,
+                                          int *min, int *max, int *maxdata);
+
+       int (*me_device_query_subdevice_by_type) (struct me_device * device,
+                                                 int start_subdevice,
+                                                 int type,
+                                                 int subtype, int *subdevice);
+
+       int (*me_device_query_subdevice_type) (struct me_device * device,
+                                              int subdevice,
+                                              int *type, int *subtype);
+
+       int (*me_device_query_subdevice_caps) (struct me_device * device,
+                                              int subdevice, int *caps);
+
+       int (*me_device_query_subdevice_caps_args) (struct me_device * device,
+                                                   int subdevice,
+                                                   int cap,
+                                                   int *args, int count);
+
+       int (*me_device_query_timer) (struct me_device * device,
+                                     int subdevice,
+                                     int timer,
+                                     int *base_frequency,
+                                     uint64_t * min_ticks,
+                                     uint64_t * max_ticks);
+
+       int (*me_device_query_version_device_driver) (struct me_device * device,
+                                                     int *version);
+
+       int (*me_device_config_load) (struct me_device * device,
+                                     struct file * filep,
+                                     me_cfg_device_entry_t * config);
+
+       void (*me_device_destructor) (struct me_device * device);
+} me_device_t;
+
+/**
+ * @brief Initializes a PCI device base class structure.
+ *
+ * @param pci_device The PCI device context as handed over by kernel.
+ *
+ * @return 0 on success.
+ */
+int me_device_pci_init(me_device_t * me_device, struct pci_dev *pci_device);
+
+/**
+ * @brief Initializes a USB device base class structure.
+ *
+ * @param usb_interface The USB device interface as handed over by kernel.
+ *
+ * @return 0 on success.
+ */
+//int me_device_usb_init(me_device_t *me_device, struct usb_interface *interface);
+
+/**
+  * @brief Deinitializes a device base class structure and frees any previously
+  * requested resources related with this structure. It also frees any subdevice
+  * instance hold by the subdevice list.
+  *
+  * @param me_device The device class to deinitialize.
+  */
+void me_device_deinit(me_device_t * me_device);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/medlist.c b/drivers/staging/meilhaus/medlist.c
new file mode 100644 (file)
index 0000000..ef4e369
--- /dev/null
@@ -0,0 +1,127 @@
+/**
+ * @file me_dlist.c
+ *
+ * @brief Implements the device list class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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.
+ */
+
+#include "meerror.h"
+#include "medefines.h"
+
+#include "medlist.h"
+#include "medebug.h"
+
+int me_dlist_query_number_devices(struct me_dlist *dlist, int *number)
+{
+       PDEBUG_LOCKS("called.\n");
+       *number = dlist->n;
+       return ME_ERRNO_SUCCESS;
+}
+
+unsigned int me_dlist_get_number_devices(struct me_dlist *dlist)
+{
+       PDEBUG_LOCKS("called.\n");
+       return dlist->n;
+}
+
+me_device_t *me_dlist_get_device(struct me_dlist * dlist, unsigned int index)
+{
+
+       struct list_head *pos;
+       me_device_t *device = NULL;
+       unsigned int i = 0;
+
+       PDEBUG_LOCKS("called.\n");
+
+       if (index >= dlist->n) {
+               PERROR("Index out of range.\n");
+               return NULL;
+       }
+
+       list_for_each(pos, &dlist->head) {
+               if (i == index) {
+                       device = list_entry(pos, me_device_t, list);
+                       break;
+               }
+
+               ++i;
+       }
+
+       return device;
+}
+
+void me_dlist_add_device_tail(struct me_dlist *dlist, me_device_t * device)
+{
+       PDEBUG_LOCKS("called.\n");
+
+       list_add_tail(&device->list, &dlist->head);
+       ++dlist->n;
+}
+
+me_device_t *me_dlist_del_device_tail(struct me_dlist *dlist)
+{
+
+       struct list_head *last;
+       me_device_t *device;
+
+       PDEBUG_LOCKS("called.\n");
+
+       if (list_empty(&dlist->head))
+               return NULL;
+
+       last = dlist->head.prev;
+
+       device = list_entry(last, me_device_t, list);
+
+       list_del(last);
+
+       --dlist->n;
+
+       return device;
+}
+
+int me_dlist_init(me_dlist_t * dlist)
+{
+       PDEBUG_LOCKS("called.\n");
+
+       INIT_LIST_HEAD(&dlist->head);
+       dlist->n = 0;
+       return 0;
+}
+
+void me_dlist_deinit(me_dlist_t * dlist)
+{
+
+       struct list_head *s;
+       me_device_t *device;
+
+       PDEBUG_LOCKS("called.\n");
+
+       while (!list_empty(&dlist->head)) {
+               s = dlist->head.next;
+               list_del(s);
+               device = list_entry(s, me_device_t, list);
+               device->me_device_destructor(device);
+       }
+
+       dlist->n = 0;
+}
diff --git a/drivers/staging/meilhaus/medlist.h b/drivers/staging/meilhaus/medlist.h
new file mode 100644 (file)
index 0000000..091c11e
--- /dev/null
@@ -0,0 +1,91 @@
+/**
+ * @file me_dlist.h
+ *
+ * @brief Provides the device list class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME_DLIST_H_
+#define _ME_DLIST_H_
+
+#include <linux/list.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The device list container.
+ */
+typedef struct me_dlist {
+       struct list_head head;          /**< The head of the internal list. */
+       unsigned int n;                 /**< The number of devices in the list. */
+} me_dlist_t;
+
+/**
+ * @brief Queries the number of devices currently inside the list.
+ *
+ * @param dlist The device list to query.
+ * @param[out] number The number of devices.
+ *
+ * @return ME-iDS error code.
+ */
+int me_dlist_query_number_devices(struct me_dlist *dlist, int *number);
+
+/**
+ * @brief Returns the number of devices currently inside the list.
+ *
+ * @param dlist The device list to query.
+ *
+ * @return The number of devices in the list.
+ */
+unsigned int me_dlist_get_number_devices(struct me_dlist *dlist);
+
+/**
+ * @brief Get a device by index.
+ *
+ * @param dlist The device list to query.
+ * @param index The index of the device to get in the list.
+ *
+ * @return The device at index if available.\n
+ *         NULL if the index is out of range.
+ */
+me_device_t *me_dlist_get_device(struct me_dlist *dlist, unsigned int index);
+
+/**
+ * @brief Adds a device to the tail of the list.
+ *
+ * @param dlist The device list to add a device to.
+ * @param device The device to add to the list.
+ */
+void me_dlist_add_device_tail(struct me_dlist *dlist, me_device_t * device);
+
+/**
+ * @brief Removes a device from the tail of the list.
+ *
+ * @param dlist The device list.
+ *
+ * @return Pointer to the removed subdeivce.\n
+ *         NULL in cases where the list was empty.
+ */
+me_device_t *me_dlist_del_device_tail(struct me_dlist *dlist);
+
+/**
+ * @brief Initializes a device list structure.
+ *
+ * @param lock The device list structure to initialize.
+ * @return 0 on success.
+ */
+int me_dlist_init(me_dlist_t * dlist);
+
+/**
+ * @brief Deinitializes a device list structure and destructs every device in it.
+ *
+ * @param dlist The device list structure to deinitialize.
+ * @return 0 on success.
+ */
+void me_dlist_deinit(me_dlist_t * dlist);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/medlock.c b/drivers/staging/meilhaus/medlock.c
new file mode 100644 (file)
index 0000000..f649e3d
--- /dev/null
@@ -0,0 +1,195 @@
+/**
+ * @file medlock.c
+ *
+ * @brief Implements the device lock class.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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.
+ */
+
+#include <linux/spinlock.h>
+
+#include "medefines.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "meslist.h"
+#include "mesubdevice.h"
+#include "medlock.h"
+
+int me_dlock_enter(struct me_dlock *dlock, struct file *filep)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       spin_lock(&dlock->spin_lock);
+
+       if ((dlock->filep) != NULL && (dlock->filep != filep)) {
+               PERROR("Device is locked by another process.\n");
+               spin_unlock(&dlock->spin_lock);
+               return ME_ERRNO_LOCKED;
+       }
+
+       dlock->count++;
+
+       spin_unlock(&dlock->spin_lock);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+int me_dlock_exit(struct me_dlock *dlock, struct file *filep)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       spin_lock(&dlock->spin_lock);
+       dlock->count--;
+       spin_unlock(&dlock->spin_lock);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+int me_dlock_lock(struct me_dlock *dlock,
+                 struct file *filep, int lock, int flags, me_slist_t * slist)
+{
+       int err = ME_ERRNO_SUCCESS;
+       int i;
+       me_subdevice_t *subdevice;
+
+       PDEBUG_LOCKS("executed.\n");
+
+       spin_lock(&dlock->spin_lock);
+
+       switch (lock) {
+
+       case ME_LOCK_RELEASE:
+               if ((dlock->filep == filep) || (dlock->filep == NULL)) {
+                       dlock->filep = NULL;
+
+                       /* Unlock all possibly locked subdevices. */
+
+                       for (i = 0; i < me_slist_get_number_subdevices(slist);
+                            i++) {
+                               subdevice = me_slist_get_subdevice(slist, i);
+
+                               if (subdevice)
+                                       err =
+                                           subdevice->
+                                           me_subdevice_lock_subdevice
+                                           (subdevice, filep, ME_LOCK_RELEASE,
+                                            flags);
+                               else
+                                       err = ME_ERRNO_INTERNAL;
+                       }
+               }
+
+               break;
+
+       case ME_LOCK_SET:
+               if (dlock->count) {
+                       PERROR("Device is used by another process.\n");
+                       err = ME_ERRNO_USED;
+               } else if ((dlock->filep != NULL) && (dlock->filep != filep)) {
+                       PERROR("Device is locked by another process.\n");
+                       err = ME_ERRNO_LOCKED;
+               } else if (dlock->filep == NULL) {
+                       /* Check any subdevice is locked by another process. */
+
+                       for (i = 0; i < me_slist_get_number_subdevices(slist);
+                            i++) {
+                               subdevice = me_slist_get_subdevice(slist, i);
+
+                               if (subdevice) {
+                                       if ((err =
+                                            subdevice->
+                                            me_subdevice_lock_subdevice
+                                            (subdevice, filep, ME_LOCK_CHECK,
+                                             flags))) {
+                                               PERROR
+                                                   ("A subdevice is locked by another process.\n");
+                                               break;
+                                       }
+                               } else {
+                                       err = ME_ERRNO_INTERNAL;
+                               }
+                       }
+
+                       /* If no subdevices are locked by other processes,
+                          we can take ownership of the device. Otherwise we jump ahead. */
+                       if (!err)
+                               dlock->filep = filep;
+               }
+
+               break;
+
+       case ME_LOCK_CHECK:
+               if (dlock->count) {
+                       err = ME_ERRNO_USED;
+               } else if ((dlock->filep != NULL) && (dlock->filep != filep)) {
+                       err = ME_ERRNO_LOCKED;
+               } else if (dlock->filep == NULL) {
+                       for (i = 0; i < me_slist_get_number_subdevices(slist);
+                            i++) {
+                               subdevice = me_slist_get_subdevice(slist, i);
+
+                               if (subdevice) {
+                                       if ((err =
+                                            subdevice->
+                                            me_subdevice_lock_subdevice
+                                            (subdevice, filep, ME_LOCK_CHECK,
+                                             flags))) {
+                                               PERROR
+                                                   ("A subdevice is locked by another process.\n");
+                                               break;
+                                       }
+                               } else {
+                                       err = ME_ERRNO_INTERNAL;
+                               }
+                       }
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid lock.\n");
+
+               err = ME_ERRNO_INVALID_LOCK;
+
+               break;
+       }
+
+       spin_unlock(&dlock->spin_lock);
+
+       return err;
+}
+
+void me_dlock_deinit(struct me_dlock *dlock)
+{
+       PDEBUG_LOCKS("executed.\n");
+}
+
+int me_dlock_init(me_dlock_t * dlock)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       dlock->filep = NULL;
+       dlock->count = 0;
+       spin_lock_init(&dlock->spin_lock);
+
+       return 0;
+}
diff --git a/drivers/staging/meilhaus/medlock.h b/drivers/staging/meilhaus/medlock.h
new file mode 100644 (file)
index 0000000..4d6ddc8
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * @file medlock.h
+ *
+ * @brief Provides the device lock class.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _MEDLOCK_H_
+#define _MEDLOCK_H_
+
+#include <linux/spinlock.h>
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The device lock class.
+ */
+typedef struct me_dlock {
+       struct file *filep;             /**< Pointer to file structure holding the device. */
+       int count;                              /**< Number of tasks which are inside the device. */
+       spinlock_t spin_lock;   /**< Spin lock protecting the attributes from concurrent access. */
+} me_dlock_t;
+
+/**
+ * @brief Tries to enter a device.
+ *
+ * @param dlock The device lock instance.
+ * @param filep The file structure identifying the calling process.
+ *
+ * @return 0 on success.
+ */
+int me_dlock_enter(struct me_dlock *dlock, struct file *filep);
+
+/**
+ * @brief Exits a device.
+ *
+ * @param dlock The device lock instance.
+ * @param filep The file structure identifying the calling process.
+ *
+ * @return 0 on success.
+ */
+int me_dlock_exit(struct me_dlock *dlock, struct file *filep);
+
+/**
+ * @brief Tries to perform a locking action on a device.
+ *
+ * @param dlock The device lock instance.
+ * @param filep The file structure identifying the calling process.
+ * @param The action to be done.
+ * @param flags Flags from user space.
+ * @param slist The subdevice list of the device.
+ *
+ * @return 0 on success.
+ */
+int me_dlock_lock(struct me_dlock *dlock,
+                 struct file *filep, int lock, int flags, me_slist_t * slist);
+
+/**
+ * @brief Initializes a lock structure.
+ *
+ * @param dlock The lock structure to initialize.
+ * @return 0 on success.
+ */
+int me_dlock_init(me_dlock_t * dlock);
+
+/**
+ * @brief Deinitializes a lock structure.
+ *
+ * @param dlock The lock structure to deinitialize.
+ * @return 0 on success.
+ */
+void me_dlock_deinit(me_dlock_t * dlock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/medriver.h b/drivers/staging/meilhaus/medriver.h
new file mode 100644 (file)
index 0000000..02e2408
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : medriver.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ *  Author:    Krzysztof Gantzke       <k.gantzke@meilhaus.de>
+ */
+
+#ifndef _MEDRIVER_H_
+#define _MEDRIVER_H_
+
+#include "metypes.h"
+#include "meerror.h"
+#include "medefines.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+       /*===========================================================================
+         Functions to access the driver system
+         =========================================================================*/
+
+       int meOpen(int iFlags);
+       int meClose(int iFlags);
+
+       int meLockDriver(int iLock, int iFlags);
+       int meLockDevice(int iDevice, int iLock, int iFlags);
+       int meLockSubdevice(int iDevice, int iSubdevice, int iLock, int iFlags);
+
+       /*===========================================================================
+         Error handling functions
+         =========================================================================*/
+
+       int meErrorGetLastMessage(char *pcErrorMsg, int iCount);
+       int meErrorGetMessage(int iErrorCode, char *pcErrorMsg, int iCount);
+       int meErrorSetDefaultProc(int iSwitch);
+       int meErrorSetUserProc(meErrorCB_t pErrorProc);
+
+
+       /*===========================================================================
+         Functions to perform I/O on a device
+         =========================================================================*/
+
+       int meIOIrqSetCallback(
+                       int iDevice,
+                       int iSubdevice,
+                       meIOIrqCB_t pCallback,
+                       void *pCallbackContext,
+                       int iFlags);
+       int meIOIrqStart(
+                       int iDevice,
+                       int iSubdevice,
+                       int iChannel,
+                       int iIrqSource,
+                       int iIrqEdge,
+                       int iIrqArg,
+                       int iFlags);
+       int meIOIrqStop(
+                       int iDevice,
+                       int iSubdevice,
+                       int iChannel,
+                       int iFlags);
+       int meIOIrqWait(
+                       int iDevice,
+                       int iSubdevice,
+                       int iChannel,
+                       int *piIrqCount,
+                       int *piValue,
+                       int iTimeOut,
+                       int iFlags);
+
+       int meIOResetDevice(int iDevice, int iFlags);
+       int meIOResetSubdevice(int iDevice, int iSubdevice, int iFlags);
+
+       int meIOStreamFrequencyToTicks(
+                       int iDevice,
+                       int iSubdevice,
+                       int iTimer,
+                       double *pdFrequency,
+                       int *piTicksLow,
+                       int *piTicksHigh,
+                       int iFlags);
+
+       int meIOSingleConfig(
+                       int iDevice,
+                       int iSubdevice,
+                       int iChannel,
+                       int iSingleConfig,
+                       int iRef,
+                       int iTrigChan,
+                       int iTrigType,
+                       int iTrigEdge,
+                       int iFlags);
+       int meIOSingle(meIOSingle_t *pSingleList, int iCount, int iFlags);
+
+       int meIOStreamConfig(
+                       int iDevice,
+                       int iSubdevice,
+                       meIOStreamConfig_t *pConfigList,
+                       int iCount,
+                       meIOStreamTrigger_t *pTrigger,
+                       int iFifoIrqThreshold,
+                       int iFlags);
+       int meIOStreamNewValues(
+                       int iDevice,
+                       int iSubdevice,
+                       int iTimeOut,
+                       int *piCount,
+                       int iFlags);
+       int meIOStreamRead(
+                       int iDevice,
+                       int iSubdevice,
+                       int iReadMode,
+                       int *piValues,
+                       int *piCount,
+                       int iFlags);
+       int meIOStreamWrite(
+                       int iDevice,
+                       int iSubdevice,
+                       int iWriteMode,
+                       int *piValues,
+                       int *piCount,
+                       int iFlags);
+       int meIOStreamStart(meIOStreamStart_t *pStartList, int iCount, int iFlags);
+       int meIOStreamStop(meIOStreamStop_t *pStopList, int iCount, int iFlags);
+       int meIOStreamStatus(
+                       int iDevice,
+                       int iSubdevice,
+                       int iWait,
+                       int *piStatus,
+                       int *piCount,
+                       int iFlags);
+       int meIOStreamSetCallbacks(
+                       int iDevice,
+                       int iSubdevice,
+                       meIOStreamCB_t pStartCB,
+                       void *pStartCBContext,
+                       meIOStreamCB_t pNewValuesCB,
+                       void *pNewValuesCBContext,
+                       meIOStreamCB_t pEndCB,
+                       void *pEndCBContext,
+                       int iFlags);
+       int meIOStreamTimeToTicks(
+                       int iDevice,
+                       int iSubdevice,
+                       int iTimer,
+                       double *pdTime,
+                       int *piTicksLow,
+                       int *piTicksHigh,
+                       int iFlags);
+
+
+       /*===========================================================================
+         Functions to query the driver system
+         =========================================================================*/
+
+       int meQueryDescriptionDevice(int iDevice, char *pcDescription, int iCount);
+
+       int meQueryInfoDevice(
+                       int iDevice,
+                       int *piVendorId,
+                       int *piDeviceId,
+                       int *piSerialNo,
+                       int *piBusType,
+                       int *piBusNo,
+                       int *piDevNo,
+                       int *piFuncNo,
+                       int *piPlugged);
+
+       int meQueryNameDevice(int iDevice, char *pcName, int iCount);
+       int meQueryNameDeviceDriver(int iDevice, char *pcName, int iCount);
+
+       int meQueryNumberDevices(int *piNumber);
+       int meQueryNumberSubdevices(int iDevice, int *piNumber);
+       int meQueryNumberChannels(int iDevice, int iSubdevice, int *piNumber);
+       int meQueryNumberRanges(
+                       int iDevice,
+                       int iSubdevice,
+                       int iUnit,
+                       int *piNumber);
+
+       int meQueryRangeByMinMax(
+                       int iDevice,
+                       int iSubdevice,
+                       int iUnit,
+                       double *pdMin,
+                       double *pdMax,
+                       int *piMaxData,
+                       int *piRange);
+       int meQueryRangeInfo(
+                       int iDevice,
+                       int iSubdevice,
+                       int iRange,
+                       int *piUnit,
+                       double *pdMin,
+                       double *pdMax,
+                       int *piMaxData);
+
+       int meQuerySubdeviceByType(
+                       int iDevice,
+                       int iStartSubdevice,
+                       int iType,
+                       int iSubtype,
+                       int *piSubdevice);
+       int meQuerySubdeviceType(
+                       int iDevice,
+                       int iSubdevice,
+                       int *piType,
+                       int *piSubtype);
+       int meQuerySubdeviceCaps(
+                       int iDevice,
+                       int iSubdevice,
+                       int *piCaps);
+       int meQuerySubdeviceCapsArgs(
+                       int iDevice,
+                       int iSubdevice,
+                       int iCap,
+                       int *piArgs,
+                       int iCount);
+
+       int meQueryVersionLibrary(int *piVersion);
+       int meQueryVersionMainDriver(int *piVersion);
+       int meQueryVersionDeviceDriver(int iDevice, int *piVersion);
+
+
+       /*===========================================================================
+         Common utility functions
+         =========================================================================*/
+
+       int meUtilityExtractValues(
+                       int iChannel,
+                       int *piAIBuffer,
+                       int iAIBufferCount,
+                       meIOStreamConfig_t *pConfigList,
+                       int iConfigListCount,
+                       int *piChanBuffer,
+                       int *piChanBufferCount);
+       int meUtilityDigitalToPhysical(
+                       double dMin,
+                       double dMax,
+                       int iMaxData,
+                       int iData,
+                       int iModuleType,
+                       double dRefValue,
+                       double *pdPhysical);
+       int meUtilityDigitalToPhysicalV(
+                       double dMin,
+                       double dMax,
+                       int iMaxData,
+                       int *piDataBuffer,
+                       int iCount,
+                       int iModuleType,
+                       double dRefValue,
+                       double *pdPhysicalBuffer);
+       int meUtilityPhysicalToDigital(
+                       double dMin,
+                       double dMax,
+                       int iMaxData,
+                       double dPhysical,
+                       int *piData);
+       int meUtilityPWMStart(
+                       int iDevice,
+                       int iSubdevice1,
+                       int iSubdevice2,
+                       int iSubdevice3,
+                       int iRef,
+                       int iPrescaler,
+                       int iDutyCycle,
+                       int iFlag);
+       int meUtilityPWMStop(int iDevice,
+                       int iSubdevice1);
+       int meUtilityPWMRestart(
+                       int iDevice,
+                       int iSubdevice1,
+                       int iRef,
+                       int iPrescaler);
+
+
+       /*===========================================================================
+         Load configuration from file into driver system
+         =========================================================================*/
+
+       int meConfigLoad(char *pcConfigFile);
+
+
+       /*===========================================================================
+         Functions to query a remote driver system
+         =========================================================================*/
+
+       int meRQueryDescriptionDevice(
+                       char *location,
+                       int iDevice,
+                       char *pcDescription,
+                       int iCount);
+
+       int meRQueryInfoDevice(
+                       char *location,
+                       int iDevice,
+                       int *piVendorId,
+                       int *piDeviceId,
+                       int *piSerialNo,
+                       int *piBusType,
+                       int *piBusNo,
+                       int *piDevNo,
+                       int *piFuncNo,
+                       int *piPlugged);
+
+       int meRQueryNameDevice(
+                       char *location,
+                       int iDevice,
+                       char *pcName,
+                       int iCount);
+
+       int meRQueryNumberDevices(char *location, int *piNumber);
+       int meRQueryNumberSubdevices(char *location, int iDevice, int *piNumber);
+       int meRQueryNumberChannels(
+                       char *location,
+                       int iDevice,
+                       int iSubdevice,
+                       int *piNumber);
+       int meRQueryNumberRanges(
+                       char *location,
+                       int iDevice,
+                       int iSubdevice,
+                       int iUnit,
+                       int *piNumber);
+
+       int meRQueryRangeInfo(
+                       char *location,
+                       int iDevice,
+                       int iSubdevice,
+                       int iRange,
+                       int *piUnit,
+                       double *pdMin,
+                       double *pdMax,
+                       int *piMaxData);
+
+       int meRQuerySubdeviceType(
+                       char *location,
+                       int iDevice,
+                       int iSubdevice,
+                       int *piType,
+                       int *piSubtype);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/drivers/staging/meilhaus/medummy.c b/drivers/staging/meilhaus/medummy.c
new file mode 100644 (file)
index 0000000..6a9f08d
--- /dev/null
@@ -0,0 +1,1266 @@
+/* Device driver for Meilhaus ME-DUMMY devices.
+ * ===========================================
+ *
+ *    Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ *    This file 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.
+ */
+
+/*
+ * User application could also include the kernel header files. But the
+ * real kernel functions are protected by #ifdef __KERNEL__.
+ */
+#ifndef __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * This must be defined before module.h is included. Not needed, when
+ * it is a built in driver.
+ */
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "meerror.h"
+#include "meinternal.h"
+
+#include "meids.h"
+#include "mecommon.h"
+#include "medevice.h"
+#include "medebug.h"
+
+#include "medummy.h"
+
+static int medummy_io_irq_start(me_device_t * device,
+                               struct file *filep,
+                               int subdevice,
+                               int channel,
+                               int irq_source,
+                               int irq_edge, int irq_arg, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_irq_wait(me_device_t * device,
+                              struct file *filep,
+                              int subdevice,
+                              int channel,
+                              int *irq_count,
+                              int *value, int timeout, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_irq_stop(me_device_t * device,
+                              struct file *filep,
+                              int subdevice, int channel, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_reset_device(me_device_t * device,
+                                  struct file *filep, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_reset_subdevice(me_device_t * device,
+                                     struct file *filep,
+                                     int subdevice, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_single_config(me_device_t * device,
+                                   struct file *filep,
+                                   int subdevice,
+                                   int channel,
+                                   int single_config,
+                                   int ref,
+                                   int trig_chan,
+                                   int trig_type, int trig_edge, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_single_read(me_device_t * device,
+                                 struct file *filep,
+                                 int subdevice,
+                                 int channel,
+                                 int *value, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_single_write(me_device_t * device,
+                                  struct file *filep,
+                                  int subdevice,
+                                  int channel,
+                                  int value, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_config(me_device_t * device,
+                                   struct file *filep,
+                                   int subdevice,
+                                   meIOStreamConfig_t * config_list,
+                                   int count,
+                                   meIOStreamTrigger_t * trigger,
+                                   int fifo_irq_threshold, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_new_values(me_device_t * device,
+                                       struct file *filep,
+                                       int subdevice,
+                                       int timeout, int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_read(me_device_t * device,
+                                 struct file *filep,
+                                 int subdevice,
+                                 int read_mode,
+                                 int *values, int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_start(me_device_t * device,
+                                  struct file *filep,
+                                  int subdevice,
+                                  int start_mode, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_status(me_device_t * device,
+                                   struct file *filep,
+                                   int subdevice,
+                                   int wait,
+                                   int *status, int *values, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_stop(me_device_t * device,
+                                 struct file *filep,
+                                 int subdevice, int stop_mode, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_io_stream_write(me_device_t * device,
+                                  struct file *filep,
+                                  int subdevice,
+                                  int write_mode,
+                                  int *values, int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_lock_device(me_device_t * device,
+                              struct file *filep, int lock, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_lock_subdevice(me_device_t * device,
+                                 struct file *filep,
+                                 int subdevice, int lock, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_description_device(me_device_t * device,
+                                           char **description)
+{
+       medummy_device_t *instance = (medummy_device_t *) device;
+
+       PDEBUG("executed.\n");
+
+//      if (instance->magic != MEDUMMY_MAGIC_NUMBER)
+//      {
+//              PERROR("Wrong magic number.\n");
+//              return ME_ERRNO_INTERNAL;
+//      }
+
+       switch (instance->device_id) {
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1000:
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1000_A:
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1000_B:
+               *description = ME1000_DESCRIPTION_DEVICE_ME1000;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400A;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400B;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400E;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400EA;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400EB;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400C;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               *description = ME1400_DESCRIPTION_DEVICE_ME1400D;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_4U:
+               *description = ME1600_DESCRIPTION_DEVICE_ME16004U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_8U:
+               *description = ME1600_DESCRIPTION_DEVICE_ME16008U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_12U:
+               *description = ME1600_DESCRIPTION_DEVICE_ME160012U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_16U:
+               *description = ME1600_DESCRIPTION_DEVICE_ME160016U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I:
+               *description = ME1600_DESCRIPTION_DEVICE_ME160016U8I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4610:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4610;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4650:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4650;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4660;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660I:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4660I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660S:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4660S;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660IS:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4660IS;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4670;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670I:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4670I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670S:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4670S;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670IS:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4670IS;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4680;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680I:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4680I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680S:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4680S;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680IS:
+               *description = ME4600_DESCRIPTION_DEVICE_ME4680IS;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6004:
+               *description = ME6000_DESCRIPTION_DEVICE_ME60004;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6008:
+               *description = ME6000_DESCRIPTION_DEVICE_ME60008;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME600F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME600016;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6014:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000I4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6018:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000I8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME601F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000I16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6034:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6038:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME603F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6104:
+               *description = ME6000_DESCRIPTION_DEVICE_ME61004;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6108:
+               *description = ME6000_DESCRIPTION_DEVICE_ME61008;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME610F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME610016;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6114:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100I4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6118:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100I8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME611F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100I16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6134:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6138:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME613F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6044:
+               *description = ME6000_DESCRIPTION_DEVICE_ME60004DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6048:
+               *description = ME6000_DESCRIPTION_DEVICE_ME60008DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME604F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME600016DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6054:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000I4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6058:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000I8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME605F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000I16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6074:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6078:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME607F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6000ISLE16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6144:
+               *description = ME6000_DESCRIPTION_DEVICE_ME61004DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6148:
+               *description = ME6000_DESCRIPTION_DEVICE_ME61008DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME614F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME610016DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6154:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100I4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6158:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100I8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME615F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100I16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6174:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6178:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME617F:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6100ISLE16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6259:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6200I9DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6359:
+               *description = ME6000_DESCRIPTION_DEVICE_ME6300I9DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0630:
+               *description = ME0600_DESCRIPTION_DEVICE_ME0630;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_A:
+               *description = ME8100_DESCRIPTION_DEVICE_ME8100A;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_B:
+               *description = ME8100_DESCRIPTION_DEVICE_ME8100B;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0940:
+               *description = ME0900_DESCRIPTION_DEVICE_ME0940;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0950:
+               *description = ME0900_DESCRIPTION_DEVICE_ME0950;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0960:
+               *description = ME0900_DESCRIPTION_DEVICE_ME0960;
+
+               break;
+/*
+               case USB_DEVICE_ID_MEPHISTO_S1:
+                       *description = MEPHISTO_S1_DESCRIPTION_DEVICE;
+
+                       break;
+*/
+       default:
+               *description = EMPTY_DESCRIPTION_DEVICE;
+               PERROR("Invalid device id in device info.\n");
+
+               return ME_ERRNO_INTERNAL;
+       }
+
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_info_device(me_device_t * device,
+                                    int *vendor_id,
+                                    int *device_id,
+                                    int *serial_no,
+                                    int *bus_type,
+                                    int *bus_no,
+                                    int *dev_no, int *func_no, int *plugged)
+{
+       medummy_device_t *instance = (medummy_device_t *) device;
+
+       PDEBUG("executed.\n");
+
+//      if (instance->magic != MEDUMMY_MAGIC_NUMBER)
+//      {
+//              PERROR("Wrong magic number.\n");
+//              return ME_ERRNO_INTERNAL;
+//      }
+
+       *vendor_id = instance->vendor_id;
+       *device_id = instance->device_id;
+       *serial_no = instance->serial_no;
+       *bus_type = instance->bus_type;
+       *bus_no = instance->bus_no;
+       *dev_no = instance->dev_no;
+       *func_no = instance->func_no;
+       *plugged = ME_PLUGGED_OUT;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int medummy_query_name_device_driver(me_device_t * device, char **name)
+{
+       PDEBUG("executed.\n");
+       *name = MEDUMMY_NAME_DRIVER;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int medummy_query_name_device(me_device_t * device, char **name)
+{
+       medummy_device_t *instance = (medummy_device_t *) device;
+
+       PDEBUG("executed.\n");
+
+// // //        if (instance->magic != MEDUMMY_MAGIC_NUMBER)
+// // //        {
+// // //                PERROR("Wrong magic number.\n");
+// // //                return ME_ERRNO_INTERNAL;
+// // //        }
+
+       switch (instance->device_id) {
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1000:
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1000_A:
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1000_B:
+               *name = ME1000_NAME_DEVICE_ME1000;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1400:
+               *name = ME1400_NAME_DEVICE_ME1400;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140A:
+               *name = ME1400_NAME_DEVICE_ME1400A;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140B:
+               *name = ME1400_NAME_DEVICE_ME1400B;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14E0:
+               *name = ME1400_NAME_DEVICE_ME1400E;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14EA:
+               *name = ME1400_NAME_DEVICE_ME1400EA;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME14EB:
+               *name = ME1400_NAME_DEVICE_ME1400EB;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140C:
+               *name = ME1400_NAME_DEVICE_ME1400C;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME140D:
+               *name = ME1400_NAME_DEVICE_ME1400D;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_4U:
+               *name = ME1600_NAME_DEVICE_ME16004U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_8U:
+               *name = ME1600_NAME_DEVICE_ME16008U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_12U:
+               *name = ME1600_NAME_DEVICE_ME160012U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_16U:
+               *name = ME1600_NAME_DEVICE_ME160016U;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I:
+               *name = ME1600_NAME_DEVICE_ME160016U8I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4610:
+               *name = ME4600_NAME_DEVICE_ME4610;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4650:
+               *name = ME4600_NAME_DEVICE_ME4650;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660:
+               *name = ME4600_NAME_DEVICE_ME4660;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4660I:
+               *name = ME4600_NAME_DEVICE_ME4660I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670:
+               *name = ME4600_NAME_DEVICE_ME4670;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670I:
+               *name = ME4600_NAME_DEVICE_ME4670I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670S:
+               *name = ME4600_NAME_DEVICE_ME4670S;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4670IS:
+               *name = ME4600_NAME_DEVICE_ME4670IS;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680:
+               *name = ME4600_NAME_DEVICE_ME4680;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680I:
+               *name = ME4600_NAME_DEVICE_ME4680I;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680S:
+               *name = ME4600_NAME_DEVICE_ME4680S;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME4680IS:
+               *name = ME4600_NAME_DEVICE_ME4680IS;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6004:
+               *name = ME6000_NAME_DEVICE_ME60004;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6008:
+               *name = ME6000_NAME_DEVICE_ME60008;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME600F:
+               *name = ME6000_NAME_DEVICE_ME600016;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6014:
+               *name = ME6000_NAME_DEVICE_ME6000I4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6018:
+               *name = ME6000_NAME_DEVICE_ME6000I8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME601F:
+               *name = ME6000_NAME_DEVICE_ME6000I16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6034:
+               *name = ME6000_NAME_DEVICE_ME6000ISLE4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6038:
+               *name = ME6000_NAME_DEVICE_ME6000ISLE8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME603F:
+               *name = ME6000_NAME_DEVICE_ME6000ISLE16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6104:
+               *name = ME6000_NAME_DEVICE_ME61004;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6108:
+               *name = ME6000_NAME_DEVICE_ME61008;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME610F:
+               *name = ME6000_NAME_DEVICE_ME610016;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6114:
+               *name = ME6000_NAME_DEVICE_ME6100I4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6118:
+               *name = ME6000_NAME_DEVICE_ME6100I8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME611F:
+               *name = ME6000_NAME_DEVICE_ME6100I16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6134:
+               *name = ME6000_NAME_DEVICE_ME6100ISLE4;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6138:
+               *name = ME6000_NAME_DEVICE_ME6100ISLE8;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME613F:
+               *name = ME6000_NAME_DEVICE_ME6100ISLE16;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6044:
+               *name = ME6000_NAME_DEVICE_ME60004DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6048:
+               *name = ME6000_NAME_DEVICE_ME60008DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME604F:
+               *name = ME6000_NAME_DEVICE_ME600016DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6054:
+               *name = ME6000_NAME_DEVICE_ME6000I4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6058:
+               *name = ME6000_NAME_DEVICE_ME6000I8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME605F:
+               *name = ME6000_NAME_DEVICE_ME6000I16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6074:
+               *name = ME6000_NAME_DEVICE_ME6000ISLE4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6078:
+               *name = ME6000_NAME_DEVICE_ME6000ISLE8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME607F:
+               *name = ME6000_NAME_DEVICE_ME6000ISLE16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6144:
+               *name = ME6000_NAME_DEVICE_ME61004DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6148:
+               *name = ME6000_NAME_DEVICE_ME61008DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME614F:
+               *name = ME6000_NAME_DEVICE_ME610016DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6154:
+               *name = ME6000_NAME_DEVICE_ME6100I4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6158:
+               *name = ME6000_NAME_DEVICE_ME6100I8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME615F:
+               *name = ME6000_NAME_DEVICE_ME6100I16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6174:
+               *name = ME6000_NAME_DEVICE_ME6100ISLE4DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME6178:
+               *name = ME6000_NAME_DEVICE_ME6100ISLE8DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME617F:
+               *name = ME6000_NAME_DEVICE_ME6100ISLE16DIO;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0630:
+               *name = ME0600_NAME_DEVICE_ME0630;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_A:
+               *name = ME8100_NAME_DEVICE_ME8100A;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME8100_B:
+               *name = ME8100_NAME_DEVICE_ME8100B;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0940:
+               *name = ME0900_NAME_DEVICE_ME0940;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0950:
+               *name = ME0900_NAME_DEVICE_ME0950;
+
+               break;
+
+       case PCI_DEVICE_ID_MEILHAUS_ME0960:
+               *name = ME0900_NAME_DEVICE_ME0960;
+
+               break;
+/*
+               case USB_DEVICE_ID_MEPHISTO_S1:
+                       *name = MEPHISTO_S1_NAME_DEVICE;
+
+                       break;
+*/
+       default:
+               *name = EMPTY_NAME_DEVICE;
+               PERROR("Invalid PCI device id.\n");
+
+               return ME_ERRNO_INTERNAL;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int medummy_query_number_subdevices(me_device_t * device, int *number)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_number_channels(me_device_t * device,
+                                        int subdevice, int *number)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_number_ranges(me_device_t * device,
+                                      int subdevice, int unit, int *count)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_subdevice_type(me_device_t * device,
+                                       int subdevice, int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_subdevice_caps(me_device_t * device,
+                                       int subdevice, int *caps)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_subdevice_caps_args(me_device_t * device,
+                                            int subdevice,
+                                            int cap, int *args, int count)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int medummy_query_subdevice_by_type(me_device_t * device,
+                                          int start_subdevice,
+                                          int type,
+                                          int subtype, int *subdevice)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_range_by_min_max(me_device_t * device,
+                                         int subdevice,
+                                         int unit,
+                                         int *min,
+                                         int *max, int *maxdata, int *range)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_range_info(me_device_t * device,
+                                   int subdevice,
+                                   int range,
+                                   int *unit, int *min, int *max, int *maxdata)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+int medummy_query_timer(me_device_t * device,
+                       int subdevice,
+                       int timer,
+                       int *base_frequency,
+                       uint64_t * min_ticks, uint64_t * max_ticks)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_DEVICE_UNPLUGGED;
+}
+
+static int medummy_query_version_device_driver(me_device_t * device,
+                                              int *version)
+{
+       PDEBUG("executed.\n");
+
+       *version = ME_VERSION_DRIVER;
+       return ME_ERRNO_SUCCESS;
+}
+
+static void medummy_destructor(me_device_t * device)
+{
+       PDEBUG("executed.\n");
+       kfree(device);
+}
+
+static int init_device_info(unsigned short vendor_id,
+                           unsigned short device_id,
+                           unsigned int serial_no,
+                           int bus_type,
+                           int bus_no,
+                           int dev_no,
+                           int func_no, medummy_device_t * instance)
+{
+       PDEBUG("executed.\n");
+
+//      instance->magic = MEDUMMY_MAGIC_NUMBER;
+       instance->vendor_id = vendor_id;
+       instance->device_id = device_id;
+       instance->serial_no = serial_no;
+       instance->bus_type = bus_type;
+       instance->bus_no = bus_no;
+       instance->dev_no = dev_no;
+       instance->func_no = func_no;
+
+       return 0;
+}
+
+static int medummy_config_load(me_device_t * device, struct file *filep,
+                              me_cfg_device_entry_t * config)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_SUCCESS;
+}
+
+static int init_device_instance(me_device_t * device)
+{
+       PDEBUG("executed.\n");
+
+       INIT_LIST_HEAD(&device->list);
+
+       device->me_device_io_irq_start = medummy_io_irq_start;
+       device->me_device_io_irq_wait = medummy_io_irq_wait;
+       device->me_device_io_irq_stop = medummy_io_irq_stop;
+       device->me_device_io_reset_device = medummy_io_reset_device;
+       device->me_device_io_reset_subdevice = medummy_io_reset_subdevice;
+       device->me_device_io_single_config = medummy_io_single_config;
+       device->me_device_io_single_read = medummy_io_single_read;
+       device->me_device_io_single_write = medummy_io_single_write;
+       device->me_device_io_stream_config = medummy_io_stream_config;
+       device->me_device_io_stream_new_values = medummy_io_stream_new_values;
+       device->me_device_io_stream_read = medummy_io_stream_read;
+       device->me_device_io_stream_start = medummy_io_stream_start;
+       device->me_device_io_stream_status = medummy_io_stream_status;
+       device->me_device_io_stream_stop = medummy_io_stream_stop;
+       device->me_device_io_stream_write = medummy_io_stream_write;
+
+       device->me_device_lock_device = medummy_lock_device;
+       device->me_device_lock_subdevice = medummy_lock_subdevice;
+
+       device->me_device_query_description_device =
+           medummy_query_description_device;
+       device->me_device_query_info_device = medummy_query_info_device;
+       device->me_device_query_name_device_driver =
+           medummy_query_name_device_driver;
+       device->me_device_query_name_device = medummy_query_name_device;
+
+       device->me_device_query_number_subdevices =
+           medummy_query_number_subdevices;
+       device->me_device_query_number_channels = medummy_query_number_channels;
+       device->me_device_query_number_ranges = medummy_query_number_ranges;
+
+       device->me_device_query_range_by_min_max =
+           medummy_query_range_by_min_max;
+       device->me_device_query_range_info = medummy_query_range_info;
+
+       device->me_device_query_subdevice_type = medummy_query_subdevice_type;
+       device->me_device_query_subdevice_by_type =
+           medummy_query_subdevice_by_type;
+       device->me_device_query_subdevice_caps = medummy_query_subdevice_caps;
+       device->me_device_query_subdevice_caps_args =
+           medummy_query_subdevice_caps_args;
+
+       device->me_device_query_timer = medummy_query_timer;
+
+       device->me_device_query_version_device_driver =
+           medummy_query_version_device_driver;
+
+       device->me_device_destructor = medummy_destructor;
+       device->me_device_config_load = medummy_config_load;
+       return 0;
+}
+
+me_device_t *medummy_constructor(unsigned short vendor_id,
+                                unsigned short device_id,
+                                unsigned int serial_no,
+                                int bus_type,
+                                int bus_no, int dev_no, int func_no)
+{
+       int result = 0;
+       medummy_device_t *instance;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate structure for device attributes */
+       instance = kmalloc(sizeof(medummy_device_t), GFP_KERNEL);
+
+       if (!instance) {
+               PERROR("Can't get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(instance, 0, sizeof(medummy_device_t));
+
+       /* Initialize device info */
+       result = init_device_info(vendor_id,
+                                 device_id,
+                                 serial_no,
+                                 bus_type, bus_no, dev_no, func_no, instance);
+
+       if (result) {
+               PERROR("Cannot init baord info.\n");
+               kfree(instance);
+               return NULL;
+       }
+
+       /* Initialize device instance */
+       result = init_device_instance((me_device_t *) instance);
+
+       if (result) {
+               PERROR("Cannot init baord info.\n");
+               kfree(instance);
+               return NULL;
+       }
+
+       return (me_device_t *) instance;
+}
+
+// Init and exit of module.
+
+static int __init dummy_init(void)
+{
+       PDEBUG("executed.\n");
+       return 0;
+}
+
+static void __exit dummy_exit(void)
+{
+       PDEBUG("executed.\n");
+}
+
+module_init(dummy_init);
+
+module_exit(dummy_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for Meilhaus ME-DUMMY Devices");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-DUMMY Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(medummy_constructor);
diff --git a/drivers/staging/meilhaus/medummy.h b/drivers/staging/meilhaus/medummy.h
new file mode 100644 (file)
index 0000000..717000f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : medummy.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _MEDUMMY_H_
+#define _MEDUMMY_H_
+
+#include "metypes.h"
+#include "medefines.h"
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+#define MEDUMMY_MAGIC_NUMBER   0xDDDD
+
+typedef struct medummy_device {
+       me_device_t base;                       /**< The Meilhaus device base class. */
+//      int magic;                                      /**< The magic number of the structure */
+       unsigned short vendor_id;       /**< Vendor ID */
+       unsigned short device_id;       /**< Device ID */
+       unsigned int serial_no;         /**< Serial number of the device */
+       int bus_type;                           /**< Bus type */
+       int bus_no;                                     /**< Bus number */
+       int dev_no;                                     /**< Device number */
+       int func_no;                            /**< Function number */
+} medummy_device_t;
+
+me_device_t *medummy_constructor(unsigned short vendor_id,
+                                unsigned short device_id,
+                                unsigned int serial_no,
+                                int bus_type,
+                                int bus_no,
+                                int dev_no,
+                                int func_no) __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/meerror.h b/drivers/staging/meilhaus/meerror.h
new file mode 100644 (file)
index 0000000..9eda4bf
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : meerror.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ * Author      : KG (Krzysztof Gantzke)        <k.gantzke@meilhaus.de>
+ */
+
+#ifndef _MEERROR_H_
+#define _MEERROR_H_
+
+extern char *meErrorMsgTable[];
+
+#define ME_ERRNO_SUCCESS                                               0
+#define ME_ERRNO_INVALID_DEVICE                                        1
+#define ME_ERRNO_INVALID_SUBDEVICE                             2
+#define ME_ERRNO_INVALID_CHANNEL                               3
+#define ME_ERRNO_INVALID_SINGLE_CONFIG                 4
+#define ME_ERRNO_INVALID_REF                                   5
+#define ME_ERRNO_INVALID_TRIG_CHAN                             6
+#define ME_ERRNO_INVALID_TRIG_TYPE                             7
+#define ME_ERRNO_INVALID_TRIG_EDGE                             8
+#define ME_ERRNO_INVALID_TIMEOUT                               9
+#define ME_ERRNO_INVALID_FLAGS                                 10
+#define ME_ERRNO_OPEN                                                  11
+#define ME_ERRNO_CLOSE                                                 12
+#define ME_ERRNO_NOT_OPEN                                              13
+#define ME_ERRNO_INVALID_DIR                                   14
+#define ME_ERRNO_PREVIOUS_CONFIG                               15
+#define ME_ERRNO_NOT_SUPPORTED                                 16
+#define ME_ERRNO_SUBDEVICE_TYPE                                        17
+#define ME_ERRNO_USER_BUFFER_SIZE                              18
+#define ME_ERRNO_LOCKED                                                        19
+#define ME_ERRNO_NOMORE_SUBDEVICE_TYPE                 20
+#define ME_ERRNO_TIMEOUT                                               21
+#define ME_ERRNO_SIGNAL                                                        22
+#define ME_ERRNO_INVALID_IRQ_SOURCE                            23
+#define ME_ERRNO_THREAD_RUNNING                                        24
+#define ME_ERRNO_START_THREAD                                  25
+#define ME_ERRNO_CANCEL_THREAD                                 26
+#define ME_ERRNO_NO_CALLBACK                                   27
+#define ME_ERRNO_USED                                                  28
+#define ME_ERRNO_INVALID_UNIT                                  29
+#define ME_ERRNO_INVALID_MIN_MAX                               30
+#define ME_ERRNO_NO_RANGE                                              31
+#define ME_ERRNO_INVALID_RANGE                                 32
+#define ME_ERRNO_SUBDEVICE_BUSY                                        33
+#define ME_ERRNO_INVALID_LOCK                                  34
+#define ME_ERRNO_INVALID_SWITCH                                        35
+#define ME_ERRNO_INVALID_ERROR_MSG_COUNT               36
+#define ME_ERRNO_INVALID_STREAM_CONFIG                 37
+#define ME_ERRNO_INVALID_CONFIG_LIST_COUNT             38
+#define ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE   39
+#define ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE   40
+#define ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN   41
+#define ME_ERRNO_INVALID_ACQ_START_TIMEOUT             42
+#define ME_ERRNO_INVALID_ACQ_START_ARG                 43
+#define ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE  44
+#define ME_ERRNO_INVALID_SCAN_START_ARG                        45
+#define ME_ERRNO_INVALID_CONV_START_TRIG_TYPE  46
+#define ME_ERRNO_INVALID_CONV_START_ARG                        47
+#define ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE   48
+#define ME_ERRNO_INVALID_SCAN_STOP_ARG                 49
+#define ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE            50
+#define ME_ERRNO_INVALID_ACQ_STOP_ARG                  51
+#define ME_ERRNO_SUBDEVICE_NOT_RUNNING                 52
+#define ME_ERRNO_INVALID_READ_MODE                             53
+#define ME_ERRNO_INVALID_VALUE_COUNT                   54
+#define ME_ERRNO_INVALID_WRITE_MODE                            55
+#define ME_ERRNO_INVALID_TIMER                                 56
+#define ME_ERRNO_DEVICE_UNPLUGGED                              57
+#define ME_ERRNO_USED_INTERNAL                                 58
+#define ME_ERRNO_INVALID_DUTY_CYCLE                            59
+#define ME_ERRNO_INVALID_WAIT                                  60
+#define ME_ERRNO_CONNECT_REMOTE                                        61
+#define ME_ERRNO_COMMUNICATION                                 62
+#define ME_ERRNO_INVALID_SINGLE_LIST                   63
+#define ME_ERRNO_INVALID_MODULE_TYPE                   64
+#define ME_ERRNO_INVALID_START_MODE                            65
+#define ME_ERRNO_INVALID_STOP_MODE                             66
+#define ME_ERRNO_INVALID_FIFO_IRQ_THRESHOLD            67
+#define ME_ERRNO_INVALID_POINTER                               68
+#define ME_ERRNO_CREATE_EVENT                                  69
+#define ME_ERRNO_LACK_OF_RESOURCES                             70
+#define ME_ERRNO_CANCELLED                                             71
+#define ME_ERRNO_RING_BUFFER_OVERFLOW                  72
+#define ME_ERRNO_RING_BUFFER_UNDEFFLOW                 73
+#define ME_ERRNO_INVALID_IRQ_EDGE                              74
+#define ME_ERRNO_INVALID_IRQ_ARG                               75
+#define ME_ERRNO_INVALID_CAP                                   76
+#define ME_ERRNO_INVALID_CAP_ARG_COUNT                 77
+#define ME_ERRNO_INTERNAL                                              78
+
+/** New error for range check */
+#define ME_ERRNO_VALUE_OUT_OF_RANGE                            79
+#define ME_ERRNO_FIFO_BUFFER_OVERFLOW                  80
+#define ME_ERRNO_FIFO_BUFFER_UNDEFFLOW                 81
+
+#define ME_ERRNO_INVALID_ERROR_NUMBER                  82
+#endif
diff --git a/drivers/staging/meilhaus/mefirmware.c b/drivers/staging/meilhaus/mefirmware.c
new file mode 100644 (file)
index 0000000..c07d202
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * @file mefirmware.c
+ *
+ * @brief Implements the firmware handling.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/***************************************************************************
+ *   Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)     *
+ *   Copyright (C) 2007 by Krzysztof Gantzke k.gantzke@meilhaus.de         *
+ *                                                                         *
+ *   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.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef __KERNEL__
+# define __KERNEL__
+#endif
+
+#ifndef KBUILD_MODNAME
+#  define KBUILD_MODNAME KBUILD_STR(mefirmware)
+#endif
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <linux/firmware.h>
+
+#include "meplx_reg.h"
+#include "medebug.h"
+
+#include "mefirmware.h"
+
+int me_xilinx_download(unsigned long register_base_control,
+                      unsigned long register_base_data,
+                      struct device *dev, const char *firmware_name)
+{
+       int err = ME_ERRNO_FIRMWARE;
+       uint32_t value = 0;
+       int idx = 0;
+
+       const struct firmware *fw;
+
+       PDEBUG("executed.\n");
+
+       if (!firmware_name) {
+               PERROR("Request for firmware failed. No name provided. \n");
+               return err;
+       }
+
+       PINFO("Request '%s' firmware.\n", firmware_name);
+       err = request_firmware(&fw, firmware_name, dev);
+
+       if (err) {
+               PERROR("Request for firmware failed.\n");
+               return err;
+       }
+       // Set PLX local interrupt 2 polarity to high.
+       // Interrupt is thrown by init pin of xilinx.
+       outl(PLX_INTCSR_LOCAL_INT2_POL, register_base_control + PLX_INTCSR);
+
+       // Set /CS and /WRITE of the Xilinx
+       value = inl(register_base_control + PLX_ICR);
+       value |= ME_FIRMWARE_CS_WRITE;
+       outl(value, register_base_control + PLX_ICR);
+
+       // Init Xilinx with CS1
+       inl(register_base_data + ME_XILINX_CS1_REG);
+
+       // Wait for init to complete
+       udelay(20);
+
+       // Checkl /INIT pin
+       if (!
+           (inl(register_base_control + PLX_INTCSR) &
+            PLX_INTCSR_LOCAL_INT2_STATE)) {
+               PERROR("Can't init Xilinx.\n");
+               release_firmware(fw);
+               return -EIO;
+       }
+       // Reset /CS and /WRITE of the Xilinx
+       value = inl(register_base_control + PLX_ICR);
+       value &= ~ME_FIRMWARE_CS_WRITE;
+       outl(value, register_base_control + PLX_ICR);
+
+       // Download Xilinx firmware
+       udelay(10);
+
+       for (idx = 0; idx < fw->size; idx++) {
+               outl(fw->data[idx], register_base_data);
+#ifdef ME6000_v2_4
+///     This checking only for board's version 2.4
+               // Check if BUSY flag is set (low = ready, high = busy)
+               if (inl(register_base_control + PLX_ICR) &
+                   ME_FIRMWARE_BUSY_FLAG) {
+                       PERROR("Xilinx is still busy (idx = %d)\n", idx);
+                       release_firmware(fw);
+                       return -EIO;
+               }
+#endif //ME6000_v2_4
+       }
+       PDEBUG("Download finished. %d bytes written to PLX.\n", idx);
+
+       // If done flag is high download was successful
+       if (inl(register_base_control + PLX_ICR) & ME_FIRMWARE_DONE_FLAG) {
+               PDEBUG("SUCCESS. Done flag is set.\n");
+       } else {
+               PERROR("FAILURE. DONE flag is not set.\n");
+               release_firmware(fw);
+               return -EIO;
+       }
+
+       // Set /CS and /WRITE
+       value = inl(register_base_control + PLX_ICR);
+       value |= ME_FIRMWARE_CS_WRITE;
+       outl(value, register_base_control + PLX_ICR);
+
+       PDEBUG("Enable interrupts on the PCI interface.\n");
+       outl(ME_PLX_PCI_ACTIVATE, register_base_control + PLX_INTCSR);
+       release_firmware(fw);
+
+       return 0;
+}
diff --git a/drivers/staging/meilhaus/mefirmware.h b/drivers/staging/meilhaus/mefirmware.h
new file mode 100644 (file)
index 0000000..a268508
--- /dev/null
@@ -0,0 +1,57 @@
+/**
+ * @file mefirmware.h
+ *
+ * @brief Definitions of the firmware handling functions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/***************************************************************************
+ *   Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)     *
+ *   Copyright (C) 2007 by Krzysztof Gantzke k.gantzke@meilhaus.de         *
+ *                                                                         *
+ *   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.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#ifndef _MEFIRMWARE_H
+# define _MEFIRMWARE_H
+
+# ifdef __KERNEL__
+
+#define ME_ERRNO_FIRMWARE              -1
+
+/**
+* Registry
+*/
+#define ME_XILINX_CS1_REG              0x00C8
+
+/**
+* Flags (bits)
+*/
+
+#define ME_FIRMWARE_BUSY_FLAG  0x00000020
+#define ME_FIRMWARE_DONE_FLAG  0x00000004
+#define ME_FIRMWARE_CS_WRITE   0x00000100
+
+#define ME_PLX_PCI_ACTIVATE            0x43
+
+int me_xilinx_download(unsigned long register_base_control,
+                      unsigned long register_base_data,
+                      struct device *dev, const char *firmware_name);
+
+# endif        //__KERNEL__
+
+#endif //_MEFIRMWARE_H
diff --git a/drivers/staging/meilhaus/meids.h b/drivers/staging/meilhaus/meids.h
new file mode 100644 (file)
index 0000000..b3e757c
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : meids.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _MEIDS_H_
+#define _MEIDS_H_
+
+#ifdef __KERNEL__
+
+/*=============================================================================
+  Driver names
+  ===========================================================================*/
+
+#define MEMAIN_NAME                            "memain"
+#define ME1000_NAME                            "me1000"
+#define ME1400_NAME                            "me1400"
+#define ME1600_NAME                            "me1600"
+#define ME4600_NAME                            "me4600"
+#define ME6000_NAME                            "me6000"
+#define ME0600_NAME                            "me0600"        //"me630"
+#define ME8100_NAME                            "me8100"
+#define ME8200_NAME                            "me8200"
+#define ME0900_NAME                            "me0900"        //"me9x"
+//#define MEPHISTO_S1_NAME                      "mephisto_s1"
+#define MEDUMMY_NAME                   "medummy"
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/meinternal.h b/drivers/staging/meilhaus/meinternal.h
new file mode 100644 (file)
index 0000000..8d126b4
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : meinternal.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _MEINTERNAL_H_
+#define _MEINTERNAL_H_
+
+/*=============================================================================
+  PCI Vendor IDs
+  ===========================================================================*/
+
+#define PCI_VENDOR_ID_MEILHAUS                                         0x1402
+
+/*=============================================================================
+  PCI Device IDs
+  ===========================================================================*/
+
+#define PCI_DEVICE_ID_MEILHAUS_ME1000                          0x1000
+#define PCI_DEVICE_ID_MEILHAUS_ME1000_A                                0x100A
+#define PCI_DEVICE_ID_MEILHAUS_ME1000_B                                0x100B
+
+#define PCI_DEVICE_ID_MEILHAUS_ME1400                          0x1400
+#define PCI_DEVICE_ID_MEILHAUS_ME140A                          0x140A
+#define PCI_DEVICE_ID_MEILHAUS_ME140B                          0x140B
+#define PCI_DEVICE_ID_MEILHAUS_ME14E0                          0x14E0
+#define PCI_DEVICE_ID_MEILHAUS_ME14EA                          0x14EA
+#define PCI_DEVICE_ID_MEILHAUS_ME14EB                          0x14EB
+#define PCI_DEVICE_ID_MEILHAUS_ME140C                          0X140C
+#define PCI_DEVICE_ID_MEILHAUS_ME140D                          0X140D
+
+#define PCI_DEVICE_ID_MEILHAUS_ME1600_4U                       0x1604 // 4 voltage outputs
+#define PCI_DEVICE_ID_MEILHAUS_ME1600_8U                       0x1608 // 8 voltage outputs
+#define PCI_DEVICE_ID_MEILHAUS_ME1600_12U                      0x160C // 12 voltage outputs
+#define PCI_DEVICE_ID_MEILHAUS_ME1600_16U                      0x160F // 16 voltage outputs
+#define PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I           0x168F // 16 voltage/8 current o.
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4610                          0x4610 // Jekyll
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4650                          0x4650 // Low Cost version
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4660                          0x4660 // Standard version
+#define PCI_DEVICE_ID_MEILHAUS_ME4660I                         0x4661 // Isolated version
+#define PCI_DEVICE_ID_MEILHAUS_ME4660S                         0x4662 // Standard version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4660IS                                0x4663 // Isolated version with Sample and Hold
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4670                          0x4670 // Standard version
+#define PCI_DEVICE_ID_MEILHAUS_ME4670I                         0x4671 // Isolated version
+#define PCI_DEVICE_ID_MEILHAUS_ME4670S                         0x4672 // Standard version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4670IS                                0x4673 // Isolated version with Sample and Hold
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4680                          0x4680 // Standard version
+#define PCI_DEVICE_ID_MEILHAUS_ME4680I                         0x4681 // Isolated version
+#define PCI_DEVICE_ID_MEILHAUS_ME4680S                         0x4682 // Standard version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4680IS                                0x4683 // Isolated version with Sample and Hold
+
+/* ME6000 standard version */
+#define PCI_DEVICE_ID_MEILHAUS_ME6004                          0x6004
+#define PCI_DEVICE_ID_MEILHAUS_ME6008                          0x6008
+#define PCI_DEVICE_ID_MEILHAUS_ME600F                          0x600F
+
+/* ME6000 isolated version */
+#define PCI_DEVICE_ID_MEILHAUS_ME6014                          0x6014
+#define PCI_DEVICE_ID_MEILHAUS_ME6018                          0x6018
+#define PCI_DEVICE_ID_MEILHAUS_ME601F                          0x601F
+
+/* ME6000 isle version */
+#define PCI_DEVICE_ID_MEILHAUS_ME6034                          0x6034
+#define PCI_DEVICE_ID_MEILHAUS_ME6038                          0x6038
+#define PCI_DEVICE_ID_MEILHAUS_ME603F                          0x603F
+
+/* ME6000 standard version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6044                          0x6044
+#define PCI_DEVICE_ID_MEILHAUS_ME6048                          0x6048
+#define PCI_DEVICE_ID_MEILHAUS_ME604F                          0x604F
+
+/* ME6000 isolated version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6054                          0x6054
+#define PCI_DEVICE_ID_MEILHAUS_ME6058                          0x6058
+#define PCI_DEVICE_ID_MEILHAUS_ME605F                          0x605F
+
+/* ME6000 isle version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6074                          0x6074
+#define PCI_DEVICE_ID_MEILHAUS_ME6078                          0x6078
+#define PCI_DEVICE_ID_MEILHAUS_ME607F                          0x607F
+
+/* ME6100 standard version */
+#define PCI_DEVICE_ID_MEILHAUS_ME6104                          0x6104
+#define PCI_DEVICE_ID_MEILHAUS_ME6108                          0x6108
+#define PCI_DEVICE_ID_MEILHAUS_ME610F                          0x610F
+
+/* ME6100 isolated version */
+#define PCI_DEVICE_ID_MEILHAUS_ME6114                          0x6114
+#define PCI_DEVICE_ID_MEILHAUS_ME6118                          0x6118
+#define PCI_DEVICE_ID_MEILHAUS_ME611F                          0x611F
+
+/* ME6100 isle version */
+#define PCI_DEVICE_ID_MEILHAUS_ME6134                          0x6134
+#define PCI_DEVICE_ID_MEILHAUS_ME6138                          0x6138
+#define PCI_DEVICE_ID_MEILHAUS_ME613F                          0x613F
+
+/* ME6100 standard version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6144                          0x6144
+#define PCI_DEVICE_ID_MEILHAUS_ME6148                          0x6148
+#define PCI_DEVICE_ID_MEILHAUS_ME614F                          0x614F
+
+/* ME6100 isolated version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6154                          0x6154
+#define PCI_DEVICE_ID_MEILHAUS_ME6158                          0x6158
+#define PCI_DEVICE_ID_MEILHAUS_ME615F                          0x615F
+
+/* ME6100 isle version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6174                          0x6174
+#define PCI_DEVICE_ID_MEILHAUS_ME6178                          0x6178
+#define PCI_DEVICE_ID_MEILHAUS_ME617F                          0x617F
+
+/* ME6200 isolated version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6259                          0x6259
+
+/* ME6300 isolated version with DIO */
+#define PCI_DEVICE_ID_MEILHAUS_ME6359                          0x6359
+
+/* ME0630 */
+#define PCI_DEVICE_ID_MEILHAUS_ME0630                          0x0630
+
+/* ME8100 */
+#define PCI_DEVICE_ID_MEILHAUS_ME8100_A                                0x810A
+#define PCI_DEVICE_ID_MEILHAUS_ME8100_B                        0x810B
+
+/* ME8200 */
+#define PCI_DEVICE_ID_MEILHAUS_ME8200_A                                0x820A
+#define PCI_DEVICE_ID_MEILHAUS_ME8200_B                        0x820B
+
+/* ME0900 */
+#define PCI_DEVICE_ID_MEILHAUS_ME0940                          0x0940
+#define PCI_DEVICE_ID_MEILHAUS_ME0950                          0x0950
+#define PCI_DEVICE_ID_MEILHAUS_ME0960                          0x0960
+
+
+/*=============================================================================
+  USB Vendor IDs
+  ===========================================================================*/
+
+//#define USB_VENDOR_ID_MEPHISTO_S1                                    0x0403
+
+
+/*=============================================================================
+  USB Device IDs
+  ===========================================================================*/
+
+//#define USB_DEVICE_ID_MEPHISTO_S1                                    0xDCD0
+
+
+/* ME-1000 defines */
+#define ME1000_NAME_DRIVER                                                     "ME-1000"
+
+#define ME1000_NAME_DEVICE_ME1000                                      "ME-1000"
+
+#define ME1000_DESCRIPTION_DEVICE_ME1000                       "ME-1000 device, 128 digital i/o lines."
+
+/* ME-1400 defines */
+#define ME1400_NAME_DRIVER                                                     "ME-1400"
+
+#define ME1400_NAME_DEVICE_ME1400                                      "ME-1400"
+#define ME1400_NAME_DEVICE_ME1400E                                     "ME-1400E"
+#define ME1400_NAME_DEVICE_ME1400A                                     "ME-1400A"
+#define ME1400_NAME_DEVICE_ME1400EA                                    "ME-1400EA"
+#define ME1400_NAME_DEVICE_ME1400B                                     "ME-1400B"
+#define ME1400_NAME_DEVICE_ME1400EB                                    "ME-1400EB"
+#define ME1400_NAME_DEVICE_ME1400C                                     "ME-1400C"
+#define ME1400_NAME_DEVICE_ME1400D                                     "ME-1400D"
+
+#define ME1400_DESCRIPTION_DEVICE_ME1400                       "ME-1400 device, 24 digital i/o lines."
+#define ME1400_DESCRIPTION_DEVICE_ME1400E                      "ME-1400E device, 24 digital i/o lines."
+#define ME1400_DESCRIPTION_DEVICE_ME1400A                      "ME-1400A device, 24 digital i/o lines, 3 counters."
+#define ME1400_DESCRIPTION_DEVICE_ME1400EA                     "ME-1400EA device, 24 digital i/o lines, 3 counters."
+#define ME1400_DESCRIPTION_DEVICE_ME1400B                      "ME-1400B device, 48 digital i/o lines, 6 counters."
+#define ME1400_DESCRIPTION_DEVICE_ME1400EB                     "ME-1400EB device, 48 digital i/o lines, 6 counters."
+#define ME1400_DESCRIPTION_DEVICE_ME1400C                      "ME-1400C device, 24 digital i/o lines, 15 counters."
+#define ME1400_DESCRIPTION_DEVICE_ME1400D                      "ME-1400D device, 48 digital i/o lines, 30 counters."
+
+/* ME-1600 defines */
+#define ME1600_NAME_DRIVER                                                     "ME-1600"
+
+#define ME1600_NAME_DEVICE_ME16004U                                    "ME-1600/4U"
+#define ME1600_NAME_DEVICE_ME16008U                                    "ME-1600/8U"
+#define ME1600_NAME_DEVICE_ME160012U                           "ME-1600/12U"
+#define ME1600_NAME_DEVICE_ME160016U                           "ME-1600/16U"
+#define ME1600_NAME_DEVICE_ME160016U8I                         "ME-1600/16U8I"
+
+#define ME1600_DESCRIPTION_DEVICE_ME16004U                     "ME-1600/4U device, 4 voltage outputs."
+#define ME1600_DESCRIPTION_DEVICE_ME16008U                     "ME-1600/8U device, 8 voltage outputs."
+#define ME1600_DESCRIPTION_DEVICE_ME160012U                    "ME-1600/12U device, 12 voltage outputs."
+#define ME1600_DESCRIPTION_DEVICE_ME160016U                    "ME-1600/16U device, 16 voltage outputs."
+#define ME1600_DESCRIPTION_DEVICE_ME160016U8I          "ME-1600/16U8I device, 16 voltage, 8 current outputs."
+
+/* ME-4000 defines */
+#define ME4600_NAME_DRIVER                                                     "ME-4600"
+
+#define ME4600_NAME_DEVICE_ME4610                                      "ME-4610"
+#define ME4600_NAME_DEVICE_ME4650                                      "ME-4650"
+#define ME4600_NAME_DEVICE_ME4660                                      "ME-4660"
+#define ME4600_NAME_DEVICE_ME4660I                                     "ME-4660I"
+#define ME4600_NAME_DEVICE_ME4660S                                     "ME-4660S"
+#define ME4600_NAME_DEVICE_ME4660IS                                    "ME-4660IS"
+#define ME4600_NAME_DEVICE_ME4670                                      "ME-4670"
+#define ME4600_NAME_DEVICE_ME4670I                                     "ME-4670I"
+#define ME4600_NAME_DEVICE_ME4670S                                     "ME-4670S"
+#define ME4600_NAME_DEVICE_ME4670IS                                    "ME-4670IS"
+#define ME4600_NAME_DEVICE_ME4680                                      "ME-4680"
+#define ME4600_NAME_DEVICE_ME4680I                                     "ME-4680I"
+#define ME4600_NAME_DEVICE_ME4680S                                     "ME-4680S"
+#define ME4600_NAME_DEVICE_ME4680IS                                    "ME-4680IS"
+
+#define ME4600_DESCRIPTION_DEVICE_ME4610                       "ME-4610 device, 16 streaming analog inputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4650                       "ME-4650 device, 16 streaming analog inputs, 32 digital i/o lines, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4660                       "ME-4660 device, 16 streaming analog inputs, 2 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4660I                      "ME-4660I opto isolated device, 16 streaming analog inputs, 2 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4660S                      "ME-4660 device, 16 streaming analog inputs (8 S&H), 2 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4660IS                     "ME-4660I opto isolated device, 16 streaming analog inputs (8 S&H), 2 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4670                       "ME-4670 device, 32 streaming analog inputs, 4 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4670I                      "ME-4670I opto isolated device, 32 streaming analog inputs, 4 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4670S                      "ME-4670S device, 32 streaming analog inputs (8 S&H), 4 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4670IS                     "ME-4670IS opto isolated device, 32 streaming analog inputs (8 S&H), 4 single analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4680                       "ME-4680 device, 32 streaming analog inputs, 4 streaming analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4680I                      "ME-4680I opto isolated device, 32 streaming analog inputs, 4 streaming analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4680S                      "ME-4680S device, 32 streaming analog inputs, 4 streaming analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+#define ME4600_DESCRIPTION_DEVICE_ME4680IS                     "ME-4680IS opto isolated device, 32 streaming analog inputs (8 S&H), 4 streaming analog outputs, 32 digital i/o lines, 3 counters, 1 external interrupt."
+
+/* ME-6000 defines */
+#define ME6000_NAME_DRIVER                                                     "ME-6000"
+
+#define ME6000_NAME_DEVICE_ME60004                                     "ME-6000/4"
+#define ME6000_NAME_DEVICE_ME60008                                     "ME-6000/8"
+#define ME6000_NAME_DEVICE_ME600016                                    "ME-6000/16"
+#define ME6000_NAME_DEVICE_ME6000I4                                    "ME-6000I/4"
+#define ME6000_NAME_DEVICE_ME6000I8                                    "ME-6000I/8"
+#define ME6000_NAME_DEVICE_ME6000I16                           "ME-6000I/16"
+#define ME6000_NAME_DEVICE_ME6000ISLE4                         "ME-6000ISLE/4"
+#define ME6000_NAME_DEVICE_ME6000ISLE8                         "ME-6000ISLE/8"
+#define ME6000_NAME_DEVICE_ME6000ISLE16                                "ME-6000ISLE/16"
+#define ME6000_NAME_DEVICE_ME61004                                     "ME-6100/4"
+#define ME6000_NAME_DEVICE_ME61008                                     "ME-6100/8"
+#define ME6000_NAME_DEVICE_ME610016                                    "ME-6100/16"
+#define ME6000_NAME_DEVICE_ME6100I4                                    "ME-6100I/4"
+#define ME6000_NAME_DEVICE_ME6100I8                                    "ME-6100I/8"
+#define ME6000_NAME_DEVICE_ME6100I16                           "ME-6100I/16"
+#define ME6000_NAME_DEVICE_ME6100ISLE4                         "ME-6100ISLE/4"
+#define ME6000_NAME_DEVICE_ME6100ISLE8                         "ME-6100ISLE/8"
+#define ME6000_NAME_DEVICE_ME6100ISLE16                                "ME-6100ISLE/16"
+#define ME6000_NAME_DEVICE_ME60004DIO                          "ME-6000/4/DIO"
+#define ME6000_NAME_DEVICE_ME60008DIO                          "ME-6000/8/DIO"
+#define ME6000_NAME_DEVICE_ME600016DIO                         "ME-6000/16/DIO"
+#define ME6000_NAME_DEVICE_ME6000I4DIO                         "ME-6000I/4/DIO"
+#define ME6000_NAME_DEVICE_ME6000I8DIO                         "ME-6000I/8/DIO"
+#define ME6000_NAME_DEVICE_ME6000I16DIO                                "ME-6000I/16/DIO"
+#define ME6000_NAME_DEVICE_ME6000ISLE4DIO                      "ME-6000ISLE/4/DIO"
+#define ME6000_NAME_DEVICE_ME6000ISLE8DIO                      "ME-6000ISLE/8/DIO"
+#define ME6000_NAME_DEVICE_ME6000ISLE16DIO                     "ME-6000ISLE/16/DIO"
+#define ME6000_NAME_DEVICE_ME61004DIO                          "ME-6100/4/DIO"
+#define ME6000_NAME_DEVICE_ME61008DIO                          "ME-6100/8/DIO"
+#define ME6000_NAME_DEVICE_ME610016DIO                         "ME-6100/16/DIO"
+#define ME6000_NAME_DEVICE_ME6100I4DIO                         "ME-6100I/4/DIO"
+#define ME6000_NAME_DEVICE_ME6100I8DIO                         "ME-6100I/8/DIO"
+#define ME6000_NAME_DEVICE_ME6100I16DIO                                "ME-6100I/16/DIO"
+#define ME6000_NAME_DEVICE_ME6100ISLE4DIO                      "ME-6100ISLE/4/DIO"
+#define ME6000_NAME_DEVICE_ME6100ISLE8DIO                      "ME-6100ISLE/8/DIO"
+#define ME6000_NAME_DEVICE_ME6100ISLE16DIO                     "ME-6100ISLE/16/DIO"
+#define ME6000_NAME_DEVICE_ME6200I9DIO                         "ME-6200I/9/DIO"
+#define ME6000_NAME_DEVICE_ME6300I9DIO                         "ME-6300I/9/DIO"
+
+#define ME6000_DESCRIPTION_DEVICE_ME60004                      "ME-6000/4 device, 4 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME60008                      "ME-6000/8 device, 8 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME600016                     "ME-6000/16 device, 16 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME6000I4                     "ME-6000I/4 isolated device, 4 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME6000I8                     "ME-6000I/8 isolated device, 8 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME6000I16                    "ME-6000I/16 isolated device, 16 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME6000ISLE4          "ME-6000ISLE/4 isle device, 4 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME6000ISLE8          "ME-6000ISLE/8 isle device, 8 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME6000ISLE16         "ME-6000ISLE/16 isle device, 16 single analog outputs"
+#define ME6000_DESCRIPTION_DEVICE_ME61004                      "ME-6100/4 device, 4 streaming analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME61008                      "ME-6100/8 device, 4 streaming, 4 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME610016                     "ME-6100/16 device, 4 streaming, 12 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME6100I4                     "ME-6100I/4 isolated device, 4 streaming analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME6100I8                     "ME-6100I/8 isolated device, 4 streaming, 4 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME6100I16                    "ME-6100I/16 isolated device, 4 streaming, 12 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME6100ISLE4          "ME-6100ISLE/4 isle device, 4 streaming analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME6100ISLE8          "ME-6100ISLE/8 isle device, 4 streaming, 4 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME6100ISLE16         "ME-6100ISLE/16 isle device, 4 streaming, 12 single analog outputs."
+#define ME6000_DESCRIPTION_DEVICE_ME60004DIO           "ME-6000/4/DIO device, 4 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME60008DIO           "ME-6000/8/DIO device, 8 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME600016DIO          "ME-6000/16/DIO device, 8 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6000I4DIO          "ME-6000I/4/DIO isolated device, 4 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6000I8DIO          "ME-6000I/8/DIO isolated device, 8 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6000I16DIO         "ME-6000I/16/DIO isolated device, 16 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6000ISLE4DIO       "ME-6000ISLE/4/DIO isle device, 4 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6000ISLE8DIO       "ME-6000ISLE/8/DIO isle device, 8 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6000ISLE16DIO      "ME-6000ISLE/16/DIO isle device, 16 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME61004DIO           "ME-6100/4/DIO device, 4 streaming analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME61008DIO           "ME-6100/8/DIO device, 4 streaming, 4 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME610016DIO          "ME-6100/16/DIO device, 4 streaming, 12 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6100I4DIO          "ME-6100I/4/DIO isolated device, 4 streaming analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6100I8DIO          "ME-6100I/8/DIO isolated device, 4 streaming, 4 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6100I16DIO         "ME-6100I/16/DIO isolated device, 4 streaming, 12 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6100ISLE4DIO       "ME-6100ISLE/4/DIO isle device, 4 streaming analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6100ISLE8DIO       "ME-6100ISLE/8/DIO isle device, 4 streaming, 4 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6100ISLE16DIO      "ME-6100ISLE/16/DIO isle device, 4 streaming, 12 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6200I9DIO          "ME-6200I/9/DIO isolated device, 9 single analog outputs, 16 digital i/o lines."
+#define ME6000_DESCRIPTION_DEVICE_ME6300I9DIO          "ME-6300I/9/DIO isolated device, 4 streaming, 5 single analog outputs, 16 digital i/o lines."
+
+/* ME-630 defines */
+#define ME0600_NAME_DRIVER                                                     "ME-0600"
+
+#define ME0600_NAME_DEVICE_ME0630                                      "ME-630"
+
+#define ME0600_DESCRIPTION_DEVICE_ME0630                       "ME-630 device, up to 16 relay, 8 digital ttl input lines, 8 isolated digital input lines, 16 digital i/o lines, 2 external interrupts."
+
+/* ME-8100 defines */
+#define ME8100_NAME_DRIVER                                                     "ME-8100"
+
+#define ME8100_NAME_DEVICE_ME8100A                                     "ME-8100A"
+#define ME8100_NAME_DEVICE_ME8100B                                     "ME-8100B"
+
+#define ME8100_DESCRIPTION_DEVICE_ME8100A                      "ME-8100A opto isolated device, 16 digital input lines, 16 digital output lines."
+#define ME8100_DESCRIPTION_DEVICE_ME8100B                      "ME-8100B opto isolated device, 32 digital input lines, 32 digital output lines, 3 counters."
+
+/* ME-8200 defines */
+#define ME8200_NAME_DRIVER                                                     "ME-8200"
+
+#define ME8200_NAME_DEVICE_ME8200A                                     "ME-8200A"
+#define ME8200_NAME_DEVICE_ME8200B                                     "ME-8200B"
+
+#define ME8200_DESCRIPTION_DEVICE_ME8200A                      "ME-8200A opto isolated device, 8 digital output lines, 8 digital input lines, 16 digital i/o lines."
+#define ME8200_DESCRIPTION_DEVICE_ME8200B                      "ME-8200B opto isolated device, 16 digital output lines, 16 digital input lines, 16 digital i/o lines."
+
+/* ME-0900 defines */
+#define ME0900_NAME_DRIVER                                                     "ME-0900"
+
+#define ME0900_NAME_DEVICE_ME0940                                      "ME-94"
+#define ME0900_NAME_DEVICE_ME0950                                      "ME-95"
+#define ME0900_NAME_DEVICE_ME0960                                      "ME-96"
+
+#define ME0900_DESCRIPTION_DEVICE_ME0940                       "ME-94 device, 16 digital input lines, 2 external interrupt lines."
+#define ME0900_DESCRIPTION_DEVICE_ME0950                       "ME-95 device, 16 digital output lines."
+#define ME0900_DESCRIPTION_DEVICE_ME0960                       "ME-96 device, 8 digital input lines, 8 digital output lines, 2 external interrupt lines."
+
+/* ME-DUMMY defines */
+#define MEDUMMY_NAME_DRIVER                                                    "ME-Dummy"
+
+/* MEPHISTO_S1 defines */
+/*
+#define MEPHISTO_S1_NAME_DRIVER                                                "MEphisto Scope 1"
+#define MEPHISTO_S1_NAME_DEVICE                                                "MEphisto Scope 1"
+#define MEPHISTO_S1_DESCRIPTION_DEVICE                         "MEphisto Scope 1 device, 2 analog inputs, 24 digital i/o."
+*/
+/* Error defines */
+#define EMPTY_NAME_DRIVER                                                      "ME-???"
+#define EMPTY_NAME_DEVICE                                                      "ME-???"
+#define EMPTY_DESCRIPTION_DEVICE                                       "ME-??? unknown device"
+
+#endif
diff --git a/drivers/staging/meilhaus/meioctl.h b/drivers/staging/meilhaus/meioctl.h
new file mode 100644 (file)
index 0000000..6dc719f
--- /dev/null
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : meioctl.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _MEIOCTL_H_
+#define _MEIOCTL_H_
+
+
+/*=============================================================================
+  Types for the input/output ioctls
+  ===========================================================================*/
+
+typedef struct me_io_irq_start {
+       int device;
+       int subdevice;
+       int channel;
+       int irq_source;
+       int irq_edge;
+       int irq_arg;
+       int flags;
+       int errno;
+} me_io_irq_start_t;
+
+
+typedef struct me_io_irq_wait {
+       int device;
+       int subdevice;
+       int channel;
+       int irq_count;
+       int value;
+       int time_out;
+       int flags;
+       int errno;
+} me_io_irq_wait_t;
+
+
+typedef struct me_io_irq_stop {
+       int device;
+       int subdevice;
+       int channel;
+       int flags;
+       int errno;
+} me_io_irq_stop_t;
+
+
+typedef struct me_io_reset_device {
+       int device;
+       int flags;
+       int errno;
+} me_io_reset_device_t;
+
+
+typedef struct me_io_reset_subdevice {
+       int device;
+       int subdevice;
+       int flags;
+       int errno;
+} me_io_reset_subdevice_t;
+
+
+typedef struct me_io_single_config {
+       int device;
+       int subdevice;
+       int channel;
+       int single_config;
+       int ref;
+       int trig_chan;
+       int trig_type;
+       int trig_edge;
+       int flags;
+       int errno;
+} me_io_single_config_t;
+
+
+typedef struct me_io_single {
+       meIOSingle_t *single_list;
+       int count;
+       int flags;
+       int errno;
+} me_io_single_t;
+
+
+typedef struct me_io_stream_config {
+       int device;
+       int subdevice;
+       meIOStreamConfig_t *config_list;
+       int count;
+       meIOStreamTrigger_t trigger;
+       int fifo_irq_threshold;
+       int flags;
+       int errno;
+} me_io_stream_config_t;
+
+
+typedef struct me_io_stream_new_values {
+       int device;
+       int subdevice;
+       int time_out;
+       int count;
+       int flags;
+       int errno;
+} me_io_stream_new_values_t;
+
+
+typedef struct me_io_stream_read {
+       int device;
+       int subdevice;
+       int read_mode;
+       int *values;
+       int count;
+       int flags;
+       int errno;
+} me_io_stream_read_t;
+
+
+typedef struct me_io_stream_start {
+       meIOStreamStart_t *start_list;
+       int count;
+       int flags;
+       int errno;
+} me_io_stream_start_t;
+
+
+typedef struct me_io_stream_status {
+       int device;
+       int subdevice;
+       int wait;
+       int status;
+       int count;
+       int flags;
+       int errno;
+} me_io_stream_status_t;
+
+
+typedef struct me_io_stream_stop {
+       meIOStreamStop_t *stop_list;
+       int count;
+       int flags;
+       int errno;
+} me_io_stream_stop_t;
+
+
+typedef struct me_io_stream_write {
+       int device;
+       int subdevice;
+       int write_mode;
+       int *values;
+       int count;
+       int flags;
+       int errno;
+} me_io_stream_write_t;
+
+
+/*=============================================================================
+  Types for the lock ioctls
+  ===========================================================================*/
+
+typedef struct me_lock_device {
+       int device;
+       int lock;
+       int flags;
+       int errno;
+} me_lock_device_t;
+
+
+typedef struct me_lock_driver {
+       int flags;
+       int lock;
+       int errno;
+} me_lock_driver_t;
+
+
+typedef struct me_lock_subdevice {
+       int device;
+       int subdevice;
+       int lock;
+       int flags;
+       int errno;
+} me_lock_subdevice_t;
+
+
+/*=============================================================================
+  Types for the query ioctls
+  ===========================================================================*/
+
+typedef struct me_query_info_device {
+       int device;
+       int vendor_id;
+       int device_id;
+       int serial_no;
+       int bus_type;
+       int bus_no;
+       int dev_no;
+       int func_no;
+       int plugged;
+       int errno;
+} me_query_info_device_t;
+
+
+typedef struct me_query_description_device {
+       int device;
+       char *name;
+       int count;
+       int errno;
+} me_query_description_device_t;
+
+
+typedef struct me_query_name_device {
+       int device;
+       char *name;
+       int count;
+       int errno;
+} me_query_name_device_t;
+
+
+typedef struct me_query_name_device_driver {
+       int device;
+       char *name;
+       int count;
+       int errno;
+} me_query_name_device_driver_t;
+
+
+typedef struct me_query_version_main_driver {
+       int version;
+       int errno;
+} me_query_version_main_driver_t;
+
+
+typedef struct me_query_version_device_driver {
+       int device;
+       int version;
+       int errno;
+} me_query_version_device_driver_t;
+
+
+typedef struct me_query_number_devices {
+       int number;
+       int errno;
+} me_query_number_devices_t;
+
+
+typedef struct me_query_number_subdevices {
+       int device;
+       int number;
+       int errno;
+} me_query_number_subdevices_t;
+
+
+typedef struct me_query_number_channels {
+       int device;
+       int subdevice;
+       int number;
+       int errno;
+} me_query_number_channels_t;
+
+
+typedef struct me_query_number_ranges {
+       int device;
+       int subdevice;
+       int channel;
+       int unit;
+       int number;
+       int errno;
+} me_query_number_ranges_t;
+
+
+typedef struct me_query_subdevice_by_type {
+       int device;
+       int start_subdevice;
+       int type;
+       int subtype;
+       int subdevice;
+       int errno;
+} me_query_subdevice_by_type_t;
+
+
+typedef struct me_query_subdevice_type {
+       int device;
+       int subdevice;
+       int type;
+       int subtype;
+       int errno;
+} me_query_subdevice_type_t;
+
+
+typedef struct me_query_subdevice_caps {
+       int device;
+       int subdevice;
+       int caps;
+       int errno;
+} me_query_subdevice_caps_t;
+
+
+typedef struct me_query_subdevice_caps_args {
+       int device;
+       int subdevice;
+       int cap;
+       int args[8];
+       int count;
+       int errno;
+} me_query_subdevice_caps_args_t;
+
+
+typedef struct me_query_timer {
+       int device;
+       int subdevice;
+       int timer;
+       int base_frequency;
+       long long min_ticks;
+       long long max_ticks;
+       int errno;
+} me_query_timer_t;
+
+
+typedef struct me_query_range_by_min_max {
+       int device;
+       int subdevice;
+       int channel;
+       int unit;
+       int min;
+       int max;
+       int max_data;
+       int range;
+       int errno;
+} me_query_range_by_min_max_t;
+
+
+typedef struct me_query_range_info {
+       int device;
+       int subdevice;
+       int channel;
+       int unit;
+       int range;
+       int min;
+       int max;
+       int max_data;
+       int errno;
+} me_query_range_info_t;
+
+
+/*=============================================================================
+  Types for the configuration ioctls
+  ===========================================================================*/
+
+typedef struct me_cfg_tcpip_location {
+       int access_type;
+       char *remote_host;
+       int remote_device_number;
+} me_cfg_tcpip_location_t;
+
+
+typedef union me_cfg_tcpip {
+       int access_type;
+       me_cfg_tcpip_location_t location;
+} me_cfg_tcpip_t;
+
+
+typedef struct me_cfg_pci_hw_location {
+       unsigned int bus_type;
+       unsigned int bus_no;
+       unsigned int device_no;
+       unsigned int function_no;
+} me_cfg_pci_hw_location_t;
+
+/*
+typedef struct me_cfg_usb_hw_location {
+       unsigned int bus_type;
+       unsigned int root_hub_no;
+} me_cfg_usb_hw_location_t;
+*/
+
+typedef union me_cfg_hw_location {
+       unsigned int bus_type;
+       me_cfg_pci_hw_location_t pci;
+//     me_cfg_usb_hw_location_t usb;
+} me_cfg_hw_location_t;
+
+
+typedef struct me_cfg_device_info {
+       unsigned int vendor_id;
+       unsigned int device_id;
+       unsigned int serial_no;
+       me_cfg_hw_location_t hw_location;
+} me_cfg_device_info_t;
+
+
+typedef struct me_cfg_subdevice_info {
+       int type;
+       int sub_type;
+       unsigned int number_channels;
+} me_cfg_subdevice_info_t;
+
+
+typedef struct me_cfg_range_entry {
+       int unit;
+       double min;
+       double max;
+       unsigned int max_data;
+} me_cfg_range_entry_t;
+
+
+typedef struct me_cfg_mux32m_device {
+       int type;
+       int timed;
+       unsigned int ai_channel;
+       unsigned int dio_device;
+       unsigned int dio_subdevice;
+       unsigned int timer_device;
+       unsigned int timer_subdevice;
+       unsigned int mux32s_count;
+} me_cfg_mux32m_device_t;
+
+
+typedef struct me_cfg_demux32_device {
+       int type;
+       int timed;
+       unsigned int ao_channel;
+       unsigned int dio_device;
+       unsigned int dio_subdevice;
+       unsigned int timer_device;
+       unsigned int timer_subdevice;
+} me_cfg_demux32_device_t;
+
+
+typedef union me_cfg_external_device {
+       int type;
+       me_cfg_mux32m_device_t mux32m;
+       me_cfg_demux32_device_t demux32;
+} me_cfg_external_device_t;
+
+
+typedef struct me_cfg_subdevice_entry {
+       me_cfg_subdevice_info_t info;
+       me_cfg_range_entry_t *range_list;
+       unsigned int count;
+       int locked;
+       me_cfg_external_device_t external_device;
+} me_cfg_subdevice_entry_t;
+
+
+typedef struct me_cfg_device_entry {
+       me_cfg_tcpip_t tcpip;
+       me_cfg_device_info_t info;
+       me_cfg_subdevice_entry_t *subdevice_list;
+       unsigned int count;
+} me_cfg_device_entry_t;
+
+
+typedef struct me_config_load {
+       me_cfg_device_entry_t *device_list;
+       unsigned int count;
+       int errno;
+} me_config_load_t;
+
+
+/*=============================================================================
+  The ioctls of the board
+  ===========================================================================*/
+
+#define MEMAIN_MAGIC 'y'
+
+#define ME_IO_IRQ_ENABLE                               _IOR (MEMAIN_MAGIC, 1, me_io_irq_start_t)
+#define ME_IO_IRQ_WAIT                                 _IOR (MEMAIN_MAGIC, 2, me_io_irq_wait_t)
+#define ME_IO_IRQ_DISABLE                              _IOR (MEMAIN_MAGIC, 3, me_io_irq_stop_t)
+
+#define ME_IO_RESET_DEVICE                             _IOW (MEMAIN_MAGIC, 4, me_io_reset_device_t)
+#define ME_IO_RESET_SUBDEVICE                  _IOW (MEMAIN_MAGIC, 5, me_io_reset_subdevice_t)
+
+#define ME_IO_SINGLE                                   _IOWR(MEMAIN_MAGIC, 6, me_io_single_t)
+#define ME_IO_SINGLE_CONFIG                            _IOW (MEMAIN_MAGIC, 7, me_io_single_config_t)
+
+#define ME_IO_STREAM_CONFIG                            _IOW (MEMAIN_MAGIC, 8, me_io_stream_config_t)
+#define ME_IO_STREAM_NEW_VALUES                        _IOR (MEMAIN_MAGIC, 9, me_io_stream_new_values_t)
+#define ME_IO_STREAM_READ                              _IOR (MEMAIN_MAGIC, 10, me_io_stream_read_t)
+#define ME_IO_STREAM_START                             _IOW (MEMAIN_MAGIC, 11, me_io_stream_start_t)
+#define ME_IO_STREAM_STATUS                            _IOR (MEMAIN_MAGIC, 12, me_io_stream_status_t)
+#define ME_IO_STREAM_STOP                              _IOW (MEMAIN_MAGIC, 13, me_io_stream_stop_t)
+#define ME_IO_STREAM_WRITE                             _IOW (MEMAIN_MAGIC, 14, me_io_stream_write_t)
+
+#define ME_LOCK_DRIVER                                 _IOW (MEMAIN_MAGIC, 15, me_lock_driver_t)
+#define ME_LOCK_DEVICE                                 _IOW (MEMAIN_MAGIC, 16, me_lock_device_t)
+#define ME_LOCK_SUBDEVICE                              _IOW (MEMAIN_MAGIC, 17, me_lock_subdevice_t)
+
+#define ME_QUERY_DESCRIPTION_DEVICE            _IOR (MEMAIN_MAGIC, 18, me_query_description_device_t)
+
+#define ME_QUERY_INFO_DEVICE                   _IOR (MEMAIN_MAGIC, 19, me_query_info_device_t)
+
+#define ME_QUERY_NAME_DEVICE                   _IOR (MEMAIN_MAGIC, 20, me_query_name_device_t)
+#define ME_QUERY_NAME_DEVICE_DRIVER            _IOR (MEMAIN_MAGIC, 21, me_query_name_device_driver_t)
+
+#define ME_QUERY_NUMBER_DEVICES                        _IOR (MEMAIN_MAGIC, 22, me_query_number_devices_t)
+#define ME_QUERY_NUMBER_SUBDEVICES             _IOR (MEMAIN_MAGIC, 23, me_query_number_subdevices_t)
+#define ME_QUERY_NUMBER_CHANNELS               _IOR (MEMAIN_MAGIC, 24, me_query_number_channels_t)
+#define ME_QUERY_NUMBER_RANGES                 _IOR (MEMAIN_MAGIC, 25, me_query_number_ranges_t)
+
+#define ME_QUERY_RANGE_BY_MIN_MAX              _IOR (MEMAIN_MAGIC, 26, me_query_range_by_min_max_t)
+#define ME_QUERY_RANGE_INFO                            _IOR (MEMAIN_MAGIC, 27, me_query_range_info_t)
+
+#define ME_QUERY_SUBDEVICE_BY_TYPE             _IOR (MEMAIN_MAGIC, 28, me_query_subdevice_by_type_t)
+#define ME_QUERY_SUBDEVICE_TYPE                        _IOR (MEMAIN_MAGIC, 29, me_query_subdevice_type_t)
+#define ME_QUERY_SUBDEVICE_CAPS                        _IOR (MEMAIN_MAGIC, 29, me_query_subdevice_caps_t)
+#define ME_QUERY_SUBDEVICE_CAPS_ARGS   _IOR (MEMAIN_MAGIC, 30, me_query_subdevice_caps_args_t)
+
+#define ME_QUERY_TIMER                                 _IOR (MEMAIN_MAGIC, 31, me_query_timer_t)
+
+#define ME_QUERY_VERSION_DEVICE_DRIVER _IOR (MEMAIN_MAGIC, 32, me_query_version_device_driver_t)
+#define ME_QUERY_VERSION_MAIN_DRIVER   _IOR (MEMAIN_MAGIC, 33, me_query_version_main_driver_t)
+
+#define ME_CONFIG_LOAD                                 _IOWR(MEMAIN_MAGIC, 34, me_config_load_t)
+
+#endif
diff --git a/drivers/staging/meilhaus/memain.c b/drivers/staging/meilhaus/memain.c
new file mode 100644 (file)
index 0000000..6cdeb85
--- /dev/null
@@ -0,0 +1,2022 @@
+/**
+ * @file memain.c
+ *
+ * @brief Main Meilhaus device driver.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ * @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+#include <linux/pci.h>
+//#include <linux/usb.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <linux/cdev.h>
+#include <linux/rwsem.h>
+
+#include "medefines.h"
+#include "metypes.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "memain.h"
+#include "medevice.h"
+#include "meioctl.h"
+#include "mecommon.h"
+
+/* Module parameters
+*/
+
+#ifdef BOSCH
+static unsigned int me_bosch_fw = 0;
+
+# ifdef module_param
+module_param(me_bosch_fw, int, S_IRUGO);
+# else
+MODULE_PARM(me_bosch_fw, "i");
+# endif
+
+MODULE_PARM_DESC(me_bosch_fw,
+                "Flags which signals the ME-4600 driver to load the bosch firmware (default = 0).");
+#endif //BOSCH
+
+static unsigned int major = 0;
+#ifdef module_param
+module_param(major, int, S_IRUGO);
+#else
+MODULE_PARM(major, "i");
+#endif
+
+/* Global Driver Lock
+*/
+
+static struct file *me_filep = NULL;
+static int me_count = 0;
+static spinlock_t me_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_RWSEM(me_rwsem);
+
+/* Board instances are kept in a global list */
+LIST_HEAD(me_device_list);
+
+/* Prototypes
+*/
+
+static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id);
+static void me_remove_pci(struct pci_dev *dev);
+static int insert_to_device_list(me_device_t * n_device);
+static int replace_with_dummy(int vendor_id, int device_id, int serial_no);
+static void clear_device_list(void);
+static int me_open(struct inode *inode_ptr, struct file *filep);
+static int me_release(struct inode *, struct file *);
+static int me_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+//static int me_probe_usb(struct usb_interface *interface, const struct usb_device_id *id);
+//static void me_disconnect_usb(struct usb_interface *interface);
+
+/* Character device structure
+*/
+
+static struct cdev *cdevp;
+
+/* File operations provided by the module
+*/
+
+static struct file_operations me_file_operations = {
+       .owner = THIS_MODULE,
+       .ioctl = me_ioctl,
+       .open = me_open,
+       .release = me_release,
+};
+
+struct pci_driver me_pci_driver = {
+       .name = MEMAIN_NAME,
+       .id_table = me_pci_table,
+       .probe = me_probe_pci,
+       .remove = me_remove_pci
+};
+
+/* //me_usb_driver
+static struct usb_driver me_usb_driver =
+{
+       .name = MEMAIN_NAME,
+       .id_table = me_usb_table,
+       .probe = me_probe_usb,
+       .disconnect = me_disconnect_usb
+};
+*/
+
+#ifdef ME_LOCK_MULTIPLEX_TEMPLATE
+ME_LOCK_MULTIPLEX_TEMPLATE("me_lock_device",
+                          me_lock_device_t,
+                          me_lock_device,
+                          me_device_lock_device,
+                          (device, filep, karg.lock, karg.flags))
+
+    ME_LOCK_MULTIPLEX_TEMPLATE("me_lock_subdevice",
+                          me_lock_subdevice_t,
+                          me_lock_subdevice,
+                          me_device_lock_subdevice,
+                          (device, filep, karg.subdevice, karg.lock,
+                       karg.flags))
+#else
+#error macro ME_LOCK_MULTIPLEX_TEMPLATE not defined
+#endif
+
+#ifdef ME_IO_MULTIPLEX_TEMPLATE
+ME_IO_MULTIPLEX_TEMPLATE("me_io_irq_start",
+                        me_io_irq_start_t,
+                        me_io_irq_start,
+                        me_device_io_irq_start,
+                        (device,
+                         filep,
+                         karg.subdevice,
+                         karg.channel,
+                         karg.irq_source,
+                         karg.irq_edge, karg.irq_arg, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_irq_wait",
+                        me_io_irq_wait_t,
+                        me_io_irq_wait,
+                        me_device_io_irq_wait,
+                        (device,
+                     filep,
+                     karg.subdevice,
+                     karg.channel,
+                     &karg.irq_count, &karg.value, karg.time_out, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_irq_stop",
+                        me_io_irq_stop_t,
+                        me_io_irq_stop,
+                        me_device_io_irq_stop,
+                        (device,
+                     filep, karg.subdevice, karg.channel, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_reset_device",
+                        me_io_reset_device_t,
+                        me_io_reset_device,
+                        me_device_io_reset_device, (device, filep, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_reset_subdevice",
+                        me_io_reset_subdevice_t,
+                        me_io_reset_subdevice,
+                        me_device_io_reset_subdevice,
+                        (device, filep, karg.subdevice, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_single_config",
+                        me_io_single_config_t,
+                        me_io_single_config,
+                        me_device_io_single_config,
+                        (device,
+                     filep,
+                     karg.subdevice,
+                     karg.channel,
+                     karg.single_config,
+                     karg.ref,
+                     karg.trig_chan,
+                     karg.trig_type, karg.trig_edge, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_new_values",
+                        me_io_stream_new_values_t,
+                        me_io_stream_new_values,
+                        me_device_io_stream_new_values,
+                        (device,
+                     filep,
+                     karg.subdevice, karg.time_out, &karg.count, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_read",
+                        me_io_stream_read_t,
+                        me_io_stream_read,
+                        me_device_io_stream_read,
+                        (device,
+                     filep,
+                     karg.subdevice,
+                     karg.read_mode, karg.values, &karg.count, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_status",
+                        me_io_stream_status_t,
+                        me_io_stream_status,
+                        me_device_io_stream_status,
+                        (device,
+                     filep,
+                     karg.subdevice,
+                     karg.wait, &karg.status, &karg.count, karg.flags))
+
+    ME_IO_MULTIPLEX_TEMPLATE("me_io_stream_write",
+                        me_io_stream_write_t,
+                        me_io_stream_write,
+                        me_device_io_stream_write,
+                        (device,
+                     filep,
+                     karg.subdevice,
+                     karg.write_mode, karg.values, &karg.count, karg.flags))
+#else
+#error macro ME_IO_MULTIPLEX_TEMPLATE not defined
+#endif
+
+#ifdef ME_QUERY_MULTIPLEX_STR_TEMPLATE
+ME_QUERY_MULTIPLEX_STR_TEMPLATE("me_query_name_device",
+                               me_query_name_device_t,
+                               me_query_name_device,
+                               me_device_query_name_device, (device, &msg))
+
+    ME_QUERY_MULTIPLEX_STR_TEMPLATE("me_query_name_device_driver",
+                               me_query_name_device_driver_t,
+                               me_query_name_device_driver,
+                               me_device_query_name_device_driver,
+                               (device, &msg))
+
+    ME_QUERY_MULTIPLEX_STR_TEMPLATE("me_query_description_device",
+                               me_query_description_device_t,
+                               me_query_description_device,
+                               me_device_query_description_device,
+                               (device, &msg))
+#else
+#error macro ME_QUERY_MULTIPLEX_STR_TEMPLATE not defined
+#endif
+
+#ifdef ME_QUERY_MULTIPLEX_TEMPLATE
+ME_QUERY_MULTIPLEX_TEMPLATE("me_query_info_device",
+                           me_query_info_device_t,
+                           me_query_info_device,
+                           me_device_query_info_device,
+                           (device,
+                            &karg.vendor_id,
+                            &karg.device_id,
+                            &karg.serial_no,
+                            &karg.bus_type,
+                            &karg.bus_no,
+                            &karg.dev_no, &karg.func_no, &karg.plugged))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_number_subdevices",
+                           me_query_number_subdevices_t,
+                           me_query_number_subdevices,
+                           me_device_query_number_subdevices,
+                           (device, &karg.number))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_number_channels",
+                           me_query_number_channels_t,
+                           me_query_number_channels,
+                           me_device_query_number_channels,
+                           (device, karg.subdevice, &karg.number))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_by_type",
+                           me_query_subdevice_by_type_t,
+                           me_query_subdevice_by_type,
+                           me_device_query_subdevice_by_type,
+                           (device,
+                        karg.start_subdevice,
+                        karg.type, karg.subtype, &karg.subdevice))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_type",
+                           me_query_subdevice_type_t,
+                           me_query_subdevice_type,
+                           me_device_query_subdevice_type,
+                           (device, karg.subdevice, &karg.type, &karg.subtype))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_caps",
+                           me_query_subdevice_caps_t,
+                           me_query_subdevice_caps,
+                           me_device_query_subdevice_caps,
+                           (device, karg.subdevice, &karg.caps))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_subdevice_caps_args",
+                           me_query_subdevice_caps_args_t,
+                           me_query_subdevice_caps_args,
+                           me_device_query_subdevice_caps_args,
+                           (device, karg.subdevice, karg.cap, karg.args,
+                        karg.count))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_number_ranges",
+                           me_query_number_ranges_t,
+                           me_query_number_ranges,
+                           me_device_query_number_ranges,
+                           (device, karg.subdevice, karg.unit, &karg.number))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_range_by_min_max",
+                           me_query_range_by_min_max_t,
+                           me_query_range_by_min_max,
+                           me_device_query_range_by_min_max,
+                           (device,
+                        karg.subdevice,
+                        karg.unit,
+                        &karg.min, &karg.max, &karg.max_data, &karg.range))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_range_info",
+                           me_query_range_info_t,
+                           me_query_range_info,
+                           me_device_query_range_info,
+                           (device,
+                        karg.subdevice,
+                        karg.range,
+                        &karg.unit, &karg.min, &karg.max, &karg.max_data))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_timer",
+                           me_query_timer_t,
+                           me_query_timer,
+                           me_device_query_timer,
+                           (device,
+                        karg.subdevice,
+                        karg.timer,
+                        &karg.base_frequency,
+                        &karg.min_ticks, &karg.max_ticks))
+
+    ME_QUERY_MULTIPLEX_TEMPLATE("me_query_version_device_driver",
+                           me_query_version_device_driver_t,
+                           me_query_version_device_driver,
+                           me_device_query_version_device_driver,
+                           (device, &karg.version))
+#else
+#error macro ME_QUERY_MULTIPLEX_TEMPLATE not defined
+#endif
+
+/** ******************************************************************************** **/
+
+static me_device_t *get_dummy_instance(unsigned short vendor_id,
+                                      unsigned short device_id,
+                                      unsigned int serial_no,
+                                      int bus_type,
+                                      int bus_no, int dev_no, int func_no)
+{
+       int err;
+       me_dummy_constructor_t constructor = NULL;
+       me_device_t *instance;
+
+       PDEBUG("executed.\n");
+
+       if ((constructor = symbol_get(medummy_constructor)) == NULL) {
+               err = request_module(MEDUMMY_NAME);
+
+               if (err) {
+                       PERROR("Error while request for module %s.\n",
+                              MEDUMMY_NAME);
+                       return NULL;
+               }
+
+               if ((constructor = symbol_get(medummy_constructor)) == NULL) {
+                       PERROR("Can't get %s driver module constructor.\n",
+                              MEDUMMY_NAME);
+                       return NULL;
+               }
+       }
+
+       if ((instance = (*constructor) (vendor_id,
+                                       device_id,
+                                       serial_no,
+                                       bus_type,
+                                       bus_no, dev_no, func_no)) == NULL)
+               symbol_put(medummy_constructor);
+
+       return instance;
+}
+
+static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       int err;
+       me_pci_constructor_t constructor = NULL;
+#ifdef BOSCH
+       me_bosch_constructor_t constructor_bosch = NULL;
+#endif
+       me_device_t *n_device = NULL;
+       uint32_t device;
+
+       char constructor_name[24] = "me0000_pci_constructor";
+       char module_name[7] = "me0000";
+
+       PDEBUG("executed.\n");
+       device = dev->device;
+       if ((device & 0xF000) == 0x6000) {      // Exceptions: me61xx, me62xx, me63xx are handled by one driver.
+               device &= 0xF0FF;
+       }
+
+       constructor_name[2] += (char)((device >> 12) & 0x000F);
+       constructor_name[3] += (char)((device >> 8) & 0x000F);
+       PDEBUG("constructor_name: %s\n", constructor_name);
+       module_name[2] += (char)((device >> 12) & 0x000F);
+       module_name[3] += (char)((device >> 8) & 0x000F);
+       PDEBUG("module_name: %s\n", module_name);
+
+       if ((constructor =
+            (me_pci_constructor_t) __symbol_get(constructor_name)) == NULL) {
+               if (request_module(module_name)) {
+                       PERROR("Error while request for module %s.\n",
+                              module_name);
+                       return -ENODEV;
+               }
+
+               if ((constructor =
+                    (me_pci_constructor_t) __symbol_get(constructor_name)) ==
+                   NULL) {
+                       PERROR("Can't get %s driver module constructor.\n",
+                              module_name);
+                       return -ENODEV;
+               }
+       }
+#ifdef BOSCH
+       if ((device & 0xF000) == 0x4000) {      // Bosch build has differnt constructor for me4600.
+               if ((n_device =
+                    (*constructor_bosch) (dev, me_bosch_fw)) == NULL) {
+                       __symbol_put(constructor_name);
+                       PERROR
+                           ("Can't get device instance of %s driver module.\n",
+                            module_name);
+                       return -ENODEV;
+               }
+       } else {
+#endif
+               if ((n_device = (*constructor) (dev)) == NULL) {
+                       __symbol_put(constructor_name);
+                       PERROR
+                           ("Can't get device instance of %s driver module.\n",
+                            module_name);
+                       return -ENODEV;
+               }
+#ifdef BOSCH
+       }
+#endif
+
+       insert_to_device_list(n_device);
+       err =
+           n_device->me_device_io_reset_device(n_device, NULL,
+                                               ME_IO_RESET_DEVICE_NO_FLAGS);
+       if (err) {
+               PERROR("Error while reseting device.\n");
+       } else {
+               PDEBUG("Reseting device was sucessful.\n");
+       }
+       return ME_ERRNO_SUCCESS;
+}
+
+static void release_instance(me_device_t * device)
+{
+       int vendor_id;
+       int device_id;
+       int serial_no;
+       int bus_type;
+       int bus_no;
+       int dev_no;
+       int func_no;
+       int plugged;
+
+       uint32_t dev_id;
+
+       char constructor_name[24] = "me0000_pci_constructor";
+
+       PDEBUG("executed.\n");
+
+       device->me_device_query_info_device(device,
+                                           &vendor_id,
+                                           &device_id,
+                                           &serial_no,
+                                           &bus_type,
+                                           &bus_no,
+                                           &dev_no, &func_no, &plugged);
+
+       dev_id = device_id;
+       device->me_device_destructor(device);
+
+       if (plugged != ME_PLUGGED_IN) {
+               PDEBUG("release: medummy_constructor\n");
+
+               __symbol_put("medummy_constructor");
+       } else {
+               if ((dev_id & 0xF000) == 0x6000) {      // Exceptions: me61xx, me62xx, me63xx are handled by one driver.
+                       dev_id &= 0xF0FF;
+               }
+
+               constructor_name[2] += (char)((dev_id >> 12) & 0x000F);
+               constructor_name[3] += (char)((dev_id >> 8) & 0x000F);
+               PDEBUG("release: %s\n", constructor_name);
+
+               __symbol_put(constructor_name);
+       }
+}
+
+static int insert_to_device_list(me_device_t * n_device)
+{
+       me_device_t *o_device = NULL;
+
+       struct list_head *pos;
+       int n_vendor_id;
+       int n_device_id;
+       int n_serial_no;
+       int n_bus_type;
+       int n_bus_no;
+       int n_dev_no;
+       int n_func_no;
+       int n_plugged;
+       int o_vendor_id;
+       int o_device_id;
+       int o_serial_no;
+       int o_bus_type;
+       int o_bus_no;
+       int o_dev_no;
+       int o_func_no;
+       int o_plugged;
+
+       PDEBUG("executed.\n");
+
+       n_device->me_device_query_info_device(n_device,
+                                             &n_vendor_id,
+                                             &n_device_id,
+                                             &n_serial_no,
+                                             &n_bus_type,
+                                             &n_bus_no,
+                                             &n_dev_no,
+                                             &n_func_no, &n_plugged);
+
+       down_write(&me_rwsem);
+
+       list_for_each(pos, &me_device_list) {
+               o_device = list_entry(pos, me_device_t, list);
+               o_device->me_device_query_info_device(o_device,
+                                                     &o_vendor_id,
+                                                     &o_device_id,
+                                                     &o_serial_no,
+                                                     &o_bus_type,
+                                                     &o_bus_no,
+                                                     &o_dev_no,
+                                                     &o_func_no, &o_plugged);
+
+               if (o_plugged == ME_PLUGGED_OUT) {
+                       if (((o_vendor_id == n_vendor_id) &&
+                            (o_device_id == n_device_id) &&
+                            (o_serial_no == n_serial_no) &&
+                            (o_bus_type == n_bus_type)) ||
+                           ((o_vendor_id == n_vendor_id) &&
+                            (o_device_id == n_device_id) &&
+                            (o_bus_type == n_bus_type) &&
+                            (o_bus_no == n_bus_no) &&
+                            (o_dev_no == n_dev_no) &&
+                            (o_func_no == n_func_no))) {
+                               n_device->list.prev = pos->prev;
+                               n_device->list.next = pos->next;
+                               pos->prev->next = &n_device->list;
+                               pos->next->prev = &n_device->list;
+                               release_instance(o_device);
+                               break;
+                       }
+               }
+       }
+
+       if (pos == &me_device_list) {
+               list_add_tail(&n_device->list, &me_device_list);
+       }
+
+       up_write(&me_rwsem);
+
+       return 0;
+}
+
+static void me_remove_pci(struct pci_dev *dev)
+{
+       int vendor_id = dev->vendor;
+       int device_id = dev->device;
+       int subsystem_vendor = dev->subsystem_vendor;
+       int subsystem_device = dev->subsystem_device;
+       int serial_no = (subsystem_device << 16) | subsystem_vendor;
+
+       PDEBUG("executed.\n");
+
+       PINFO("Vendor id = 0x%08X\n", vendor_id);
+       PINFO("Device id = 0x%08X\n", device_id);
+       PINFO("Serial Number = 0x%08X\n", serial_no);
+
+       replace_with_dummy(vendor_id, device_id, serial_no);
+}
+
+static int replace_with_dummy(int vendor_id, int device_id, int serial_no)
+{
+
+       struct list_head *pos;
+       me_device_t *n_device = NULL;
+       me_device_t *o_device = NULL;
+       int o_vendor_id;
+       int o_device_id;
+       int o_serial_no;
+       int o_bus_type;
+       int o_bus_no;
+       int o_dev_no;
+       int o_func_no;
+       int o_plugged;
+
+       PDEBUG("executed.\n");
+
+       down_write(&me_rwsem);
+
+       list_for_each(pos, &me_device_list) {
+               o_device = list_entry(pos, me_device_t, list);
+               o_device->me_device_query_info_device(o_device,
+                                                     &o_vendor_id,
+                                                     &o_device_id,
+                                                     &o_serial_no,
+                                                     &o_bus_type,
+                                                     &o_bus_no,
+                                                     &o_dev_no,
+                                                     &o_func_no, &o_plugged);
+
+               if (o_plugged == ME_PLUGGED_IN) {
+                       if (((o_vendor_id == vendor_id) &&
+                            (o_device_id == device_id) &&
+                            (o_serial_no == serial_no))) {
+                               n_device = get_dummy_instance(o_vendor_id,
+                                                             o_device_id,
+                                                             o_serial_no,
+                                                             o_bus_type,
+                                                             o_bus_no,
+                                                             o_dev_no,
+                                                             o_func_no);
+
+                               if (!n_device) {
+                                       up_write(&me_rwsem);
+                                       PERROR("Cannot get dummy instance.\n");
+                                       return 1;
+                               }
+
+                               n_device->list.prev = pos->prev;
+
+                               n_device->list.next = pos->next;
+                               pos->prev->next = &n_device->list;
+                               pos->next->prev = &n_device->list;
+                               release_instance(o_device);
+                               break;
+                       }
+               }
+       }
+
+       up_write(&me_rwsem);
+
+       return 0;
+}
+
+static void clear_device_list(void)
+{
+
+       struct list_head *entry;
+       me_device_t *device;
+
+       // Clear the device info list .
+       down_write(&me_rwsem);
+
+       while (!list_empty(&me_device_list)) {
+               entry = me_device_list.next;
+               device = list_entry(entry, me_device_t, list);
+               list_del(entry);
+               release_instance(device);
+       }
+
+       up_write(&me_rwsem);
+}
+
+static int lock_driver(struct file *filep, int lock, int flags)
+{
+       int err = ME_ERRNO_SUCCESS;
+       me_device_t *device;
+
+       PDEBUG("executed.\n");
+
+       down_read(&me_rwsem);
+
+       spin_lock(&me_lock);
+
+       switch (lock) {
+
+       case ME_LOCK_SET:
+               if (me_count) {
+                       PERROR
+                           ("Driver System is currently used by another process.\n");
+                       err = ME_ERRNO_USED;
+               } else if ((me_filep != NULL) && (me_filep != filep)) {
+                       PERROR
+                           ("Driver System is already logged by another process.\n");
+                       err = ME_ERRNO_LOCKED;
+               } else {
+                       list_for_each_entry(device, &me_device_list, list) {
+                               err =
+                                   device->me_device_lock_device(device, filep,
+                                                                 ME_LOCK_CHECK,
+                                                                 flags);
+
+                               if (err)
+                                       break;
+                       }
+
+                       if (!err)
+                               me_filep = filep;
+               }
+
+               break;
+
+       case ME_LOCK_RELEASE:
+               if ((me_filep != NULL) && (me_filep != filep)) {
+                       err = ME_ERRNO_SUCCESS;
+               } else {
+                       list_for_each_entry(device, &me_device_list, list) {
+                               device->me_device_lock_device(device, filep,
+                                                             ME_LOCK_RELEASE,
+                                                             flags);
+                       }
+
+                       me_filep = NULL;
+               }
+
+               break;
+
+       default:
+               PERROR("Invalid lock specified.\n");
+
+               err = ME_ERRNO_INVALID_LOCK;
+
+               break;
+       }
+
+       spin_unlock(&me_lock);
+
+       up_read(&me_rwsem);
+
+       return err;
+}
+
+static int me_lock_driver(struct file *filep, me_lock_driver_t * arg)
+{
+       int err = 0;
+
+       me_lock_driver_t lock;
+
+       PDEBUG("executed.\n");
+
+       err = copy_from_user(&lock, arg, sizeof(me_lock_driver_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to kernel space.\n");
+               return -EFAULT;
+       }
+
+       lock.errno = lock_driver(filep, lock.lock, lock.flags);
+
+       err = copy_to_user(arg, &lock, sizeof(me_lock_driver_t));
+
+       if (err) {
+               PERROR("Can't copy query back to user space.\n");
+               return -EFAULT;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_open(struct inode *inode_ptr, struct file *filep)
+{
+
+       PDEBUG("executed.\n");
+       // Nothing to do here.
+       return 0;
+}
+
+static int me_release(struct inode *inode_ptr, struct file *filep)
+{
+
+       PDEBUG("executed.\n");
+       lock_driver(filep, ME_LOCK_RELEASE, ME_LOCK_DRIVER_NO_FLAGS);
+
+       return 0;
+}
+
+static int me_query_version_main_driver(struct file *filep,
+                                       me_query_version_main_driver_t * arg)
+{
+       int err;
+       me_query_version_main_driver_t karg;
+
+       PDEBUG("executed.\n");
+
+       karg.version = ME_VERSION_DRIVER;
+       karg.errno = ME_ERRNO_SUCCESS;
+
+       err = copy_to_user(arg, &karg, sizeof(me_query_version_main_driver_t));
+
+       if (err) {
+               PERROR("Can't copy query back to user space.\n");
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int me_config_load_device(struct file *filep,
+                                me_cfg_device_entry_t * karg, int device_no)
+{
+
+       int err = ME_ERRNO_SUCCESS;
+       int k = 0;
+
+       struct list_head *pos = NULL;
+       me_device_t *device = NULL;
+
+       PDEBUG("executed.\n");
+
+       list_for_each(pos, &me_device_list) {
+               if (k == device_no) {
+                       device = list_entry(pos, me_device_t, list);
+                       break;
+               }
+
+               k++;
+       }
+
+       if (pos == &me_device_list) {
+               PERROR("Invalid device number specified.\n");
+               return ME_ERRNO_INVALID_DEVICE;
+       } else {
+               spin_lock(&me_lock);
+
+               if ((me_filep != NULL) && (me_filep != filep)) {
+                       spin_unlock(&me_lock);
+                       PERROR("Resource is locked by another process.\n");
+                       return ME_ERRNO_LOCKED;
+               } else {
+                       me_count++;
+                       spin_unlock(&me_lock);
+
+                       err =
+                           device->me_device_config_load(device, filep, karg);
+
+                       spin_lock(&me_lock);
+                       me_count--;
+                       spin_unlock(&me_lock);
+               }
+       }
+
+       return err;
+}
+
+static int me_config_load(struct file *filep, me_config_load_t * arg)
+{
+       int err;
+       int i;
+       me_config_load_t cfg_setup;
+       me_config_load_t karg_cfg_setup;
+
+       struct list_head *pos = NULL;
+
+       struct list_head new_list;
+       me_device_t *o_device;
+       me_device_t *n_device;
+       int o_vendor_id;
+       int o_device_id;
+       int o_serial_no;
+       int o_bus_type;
+       int o_bus_no;
+       int o_dev_no;
+       int o_func_no;
+       int o_plugged;
+
+       PDEBUG("executed.\n");
+
+       // Copy argument to kernel space.
+       err = copy_from_user(&karg_cfg_setup, arg, sizeof(me_config_load_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to kernel space.\n");
+               return -EFAULT;
+       }
+       // Allocate kernel buffer for device list.
+       cfg_setup.device_list =
+           kmalloc(sizeof(me_cfg_device_entry_t) * karg_cfg_setup.count,
+                   GFP_KERNEL);
+
+       if (!cfg_setup.device_list) {
+               PERROR("Can't get buffer %li for device list.\n",
+                      sizeof(me_cfg_device_entry_t) * karg_cfg_setup.count);
+               return -ENOMEM;
+       }
+       // Copy device list to kernel space.
+       err =
+           copy_from_user(cfg_setup.device_list, karg_cfg_setup.device_list,
+                          sizeof(me_cfg_device_entry_t) *
+                          karg_cfg_setup.count);
+
+       if (err) {
+               PERROR("Can't copy device list to kernel space.\n");
+               kfree(cfg_setup.device_list);
+               return -EFAULT;
+       }
+
+       cfg_setup.count = karg_cfg_setup.count;
+
+       INIT_LIST_HEAD(&new_list);
+
+       down_write(&me_rwsem);
+
+       spin_lock(&me_lock);
+
+       if ((me_filep != NULL) && (me_filep != filep)) {
+               spin_unlock(&me_lock);
+               PERROR("Driver System is logged by another process.\n");
+               karg_cfg_setup.errno = ME_ERRNO_LOCKED;
+       } else {
+               me_count++;
+               spin_unlock(&me_lock);
+
+               for (i = 0; i < karg_cfg_setup.count; i++) {
+                       PDEBUG("me_config_load() device=%d.\n", i);
+                       if (cfg_setup.device_list[i].tcpip.access_type ==
+                           ME_ACCESS_TYPE_LOCAL) {
+                               list_for_each(pos, &me_device_list) {
+                                       o_device =
+                                           list_entry(pos, me_device_t, list);
+                                       o_device->
+                                           me_device_query_info_device
+                                           (o_device, &o_vendor_id,
+                                            &o_device_id, &o_serial_no,
+                                            &o_bus_type, &o_bus_no, &o_dev_no,
+                                            &o_func_no, &o_plugged);
+
+                                       if (cfg_setup.device_list[i].info.
+                                           hw_location.bus_type ==
+                                           ME_BUS_TYPE_PCI) {
+                                               if (((o_vendor_id ==
+                                                     cfg_setup.device_list[i].
+                                                     info.vendor_id)
+                                                    && (o_device_id ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        device_id)
+                                                    && (o_serial_no ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        serial_no)
+                                                    && (o_bus_type ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        hw_location.bus_type))
+                                                   ||
+                                                   ((o_vendor_id ==
+                                                     cfg_setup.device_list[i].
+                                                     info.vendor_id)
+                                                    && (o_device_id ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        device_id)
+                                                    && (o_bus_type ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        hw_location.bus_type)
+                                                    && (o_bus_no ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        hw_location.pci.bus_no)
+                                                    && (o_dev_no ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        hw_location.pci.
+                                                        device_no)
+                                                    && (o_func_no ==
+                                                        cfg_setup.
+                                                        device_list[i].info.
+                                                        hw_location.pci.
+                                                        function_no))) {
+                                                       list_move_tail(pos,
+                                                                      &new_list);
+                                                       break;
+                                               }
+                                       }
+/*
+                                       else if (cfg_setup.device_list[i].info.hw_location.bus_type == ME_BUS_TYPE_USB)
+                                       {
+                                               if (((o_vendor_id == cfg_setup.device_list[i].info.vendor_id) &&
+                                                       (o_device_id == cfg_setup.device_list[i].info.device_id) &&
+                                                       (o_serial_no == cfg_setup.device_list[i].info.serial_no) &&
+                                                       (o_bus_type == cfg_setup.device_list[i].info.hw_location.bus_type)) ||
+                                                       ((o_vendor_id == cfg_setup.device_list[i].info.vendor_id) &&
+                                                        (o_device_id == cfg_setup.device_list[i].info.device_id) &&
+                                                        (o_bus_type == cfg_setup.device_list[i].info.hw_location.bus_type) &&
+                                                        (o_bus_no == cfg_setup.device_list[i].info.hw_location.usb.root_hub_no)))
+                                               {
+                                                       list_move_tail(pos, &new_list);
+                                                       break;
+                                               }
+                                       }
+*/
+                                       else {
+                                               PERROR("Wrong bus type: %d.\n",
+                                                      cfg_setup.device_list[i].
+                                                      info.hw_location.
+                                                      bus_type);
+                                       }
+                               }
+
+                               if (pos == &me_device_list) {   // Device is not already in the list
+                                       if (cfg_setup.device_list[i].info.
+                                           hw_location.bus_type ==
+                                           ME_BUS_TYPE_PCI) {
+                                               n_device =
+                                                   get_dummy_instance
+                                                   (cfg_setup.device_list[i].
+                                                    info.vendor_id,
+                                                    cfg_setup.device_list[i].
+                                                    info.device_id,
+                                                    cfg_setup.device_list[i].
+                                                    info.serial_no,
+                                                    cfg_setup.device_list[i].
+                                                    info.hw_location.bus_type,
+                                                    cfg_setup.device_list[i].
+                                                    info.hw_location.pci.
+                                                    bus_no,
+                                                    cfg_setup.device_list[i].
+                                                    info.hw_location.pci.
+                                                    device_no,
+                                                    cfg_setup.device_list[i].
+                                                    info.hw_location.pci.
+                                                    function_no);
+
+                                               if (!n_device) {
+                                                       PERROR
+                                                           ("Can't get dummy instance.\n");
+                                                       kfree(cfg_setup.
+                                                             device_list);
+                                                       spin_lock(&me_lock);
+                                                       me_count--;
+                                                       spin_unlock(&me_lock);
+                                                       up_write(&me_rwsem);
+                                                       return -EFAULT;
+                                               }
+
+                                               list_add_tail(&n_device->list,
+                                                             &new_list);
+                                       }
+/*
+                                       else if (cfg_setup.device_list[i].info.hw_location.bus_type == ME_BUS_TYPE_USB)
+                                       {
+                                               n_device = get_dummy_instance(
+                                                              cfg_setup.device_list[i].info.vendor_id,
+                                                              cfg_setup.device_list[i].info.device_id,
+                                                              cfg_setup.device_list[i].info.serial_no,
+                                                              cfg_setup.device_list[i].info.hw_location.bus_type,
+                                                              cfg_setup.device_list[i].info.hw_location.usb.root_hub_no,
+                                                              0,
+                                                              0);
+
+                                               if (!n_device)
+                                               {
+                                                       PERROR("Can't get dummy instance.\n");
+                                                       kfree(cfg_setup.device_list);
+                                                       spin_lock(&me_lock);
+                                                       me_count--;
+                                                       spin_unlock(&me_lock);
+                                                       up_write(&me_rwsem);
+                                                       return -EFAULT;
+                                               }
+
+                                               list_add_tail(&n_device->list, &new_list);
+                                       }
+*/
+                               }
+                       } else {
+                               n_device = get_dummy_instance(0,
+                                                             0, 0, 0, 0, 0, 0);
+
+                               if (!n_device) {
+                                       PERROR("Can't get dummy instance.\n");
+                                       kfree(cfg_setup.device_list);
+                                       spin_lock(&me_lock);
+                                       me_count--;
+                                       spin_unlock(&me_lock);
+                                       up_write(&me_rwsem);
+                                       return -EFAULT;
+                               }
+
+                               list_add_tail(&n_device->list, &new_list);
+                       }
+               }
+
+               while (!list_empty(&me_device_list)) {
+                       o_device =
+                           list_entry(me_device_list.next, me_device_t, list);
+                       o_device->me_device_query_info_device(o_device,
+                                                             &o_vendor_id,
+                                                             &o_device_id,
+                                                             &o_serial_no,
+                                                             &o_bus_type,
+                                                             &o_bus_no,
+                                                             &o_dev_no,
+                                                             &o_func_no,
+                                                             &o_plugged);
+
+                       if (o_plugged == ME_PLUGGED_IN) {
+                               list_move_tail(me_device_list.next, &new_list);
+                       } else {
+                               list_del(me_device_list.next);
+                               release_instance(o_device);
+                       }
+               }
+
+               // Move temporary new list to global driver list.
+               list_splice(&new_list, &me_device_list);
+
+               karg_cfg_setup.errno = ME_ERRNO_SUCCESS;
+       }
+
+       for (i = 0; i < cfg_setup.count; i++) {
+
+               karg_cfg_setup.errno =
+                   me_config_load_device(filep, &cfg_setup.device_list[i], i);
+               if (karg_cfg_setup.errno) {
+                       PERROR("me_config_load_device(%d)=%d\n", i,
+                              karg_cfg_setup.errno);
+                       break;
+               }
+       }
+
+       spin_lock(&me_lock);
+
+       me_count--;
+       spin_unlock(&me_lock);
+       up_write(&me_rwsem);
+
+       err = copy_to_user(arg, &karg_cfg_setup, sizeof(me_config_load_t));
+
+       if (err) {
+               PERROR("Can't copy config list to user space.\n");
+               kfree(cfg_setup.device_list);
+               return -EFAULT;
+       }
+
+       kfree(cfg_setup.device_list);
+       return 0;
+}
+
+static int me_io_stream_start(struct file *filep, me_io_stream_start_t * arg)
+{
+       int err;
+       int i, k;
+
+       struct list_head *pos;
+       me_device_t *device;
+       me_io_stream_start_t karg;
+       meIOStreamStart_t *list;
+
+       PDEBUG("executed.\n");
+
+       err = copy_from_user(&karg, arg, sizeof(me_io_stream_start_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to kernel space.\n");
+               return -EFAULT;
+       }
+
+       karg.errno = ME_ERRNO_SUCCESS;
+
+       list = kmalloc(sizeof(meIOStreamStart_t) * karg.count, GFP_KERNEL);
+
+       if (!list) {
+               PERROR("Can't get buffer for start list.\n");
+               return -ENOMEM;
+       }
+
+       err =
+           copy_from_user(list, karg.start_list,
+                          sizeof(meIOStreamStart_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy start list to kernel space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       spin_lock(&me_lock);
+
+       if ((me_filep != NULL) && (me_filep != filep)) {
+               spin_unlock(&me_lock);
+               PERROR("Driver System is logged by another process.\n");
+
+               for (i = 0; i < karg.count; i++) {
+                       list[i].iErrno = ME_ERRNO_LOCKED;
+               }
+       } else {
+               me_count++;
+               spin_unlock(&me_lock);
+
+               for (i = 0; i < karg.count; i++) {
+                       down_read(&me_rwsem);
+                       k = 0;
+                       list_for_each(pos, &me_device_list) {
+                               if (k == list[i].iDevice) {
+                                       device =
+                                           list_entry(pos, me_device_t, list);
+                                       break;
+                               }
+
+                               k++;
+                       }
+
+                       if (pos == &me_device_list) {
+                               up_read(&me_rwsem);
+                               PERROR("Invalid device number specified.\n");
+                               list[i].iErrno = ME_ERRNO_INVALID_DEVICE;
+                               karg.errno = ME_ERRNO_INVALID_DEVICE;
+                               break;
+                       } else {
+                               list[i].iErrno =
+                                   device->me_device_io_stream_start(device,
+                                                                     filep,
+                                                                     list[i].
+                                                                     iSubdevice,
+                                                                     list[i].
+                                                                     iStartMode,
+                                                                     list[i].
+                                                                     iTimeOut,
+                                                                     list[i].
+                                                                     iFlags);
+
+                               if (list[i].iErrno) {
+                                       up_read(&me_rwsem);
+                                       karg.errno = list[i].iErrno;
+                                       break;
+                               }
+                       }
+
+                       up_read(&me_rwsem);
+               }
+
+               spin_lock(&me_lock);
+
+               me_count--;
+               spin_unlock(&me_lock);
+       }
+
+       err = copy_to_user(arg, &karg, sizeof(me_io_stream_start_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to user space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       err =
+           copy_to_user(karg.start_list, list,
+                        sizeof(meIOStreamStart_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy start list to user space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       kfree(list);
+
+       return err;
+}
+
+static int me_io_single(struct file *filep, me_io_single_t * arg)
+{
+       int err;
+       int i, k;
+
+       struct list_head *pos;
+       me_device_t *device;
+       me_io_single_t karg;
+       meIOSingle_t *list;
+
+       PDEBUG("executed.\n");
+
+       err = copy_from_user(&karg, arg, sizeof(me_io_single_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to kernel space.\n");
+               return -EFAULT;
+       }
+
+       karg.errno = ME_ERRNO_SUCCESS;
+
+       list = kmalloc(sizeof(meIOSingle_t) * karg.count, GFP_KERNEL);
+
+       if (!list) {
+               PERROR("Can't get buffer for single list.\n");
+               return -ENOMEM;
+       }
+
+       err =
+           copy_from_user(list, karg.single_list,
+                          sizeof(meIOSingle_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy single list to kernel space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       spin_lock(&me_lock);
+
+       if ((me_filep != NULL) && (me_filep != filep)) {
+               spin_unlock(&me_lock);
+               PERROR("Driver System is logged by another process.\n");
+
+               for (i = 0; i < karg.count; i++) {
+                       list[i].iErrno = ME_ERRNO_LOCKED;
+               }
+       } else {
+               me_count++;
+               spin_unlock(&me_lock);
+
+               for (i = 0; i < karg.count; i++) {
+                       k = 0;
+
+                       down_read(&me_rwsem);
+
+                       list_for_each(pos, &me_device_list) {
+                               if (k == list[i].iDevice) {
+                                       device =
+                                           list_entry(pos, me_device_t, list);
+                                       break;
+                               }
+
+                               k++;
+                       }
+
+                       if (pos == &me_device_list) {
+                               up_read(&me_rwsem);
+                               PERROR("Invalid device number specified.\n");
+                               list[i].iErrno = ME_ERRNO_INVALID_DEVICE;
+                               karg.errno = ME_ERRNO_INVALID_DEVICE;
+                               break;
+                       } else {
+                               if (list[i].iDir == ME_DIR_OUTPUT) {
+                                       list[i].iErrno =
+                                           device->
+                                           me_device_io_single_write(device,
+                                                                     filep,
+                                                                     list[i].
+                                                                     iSubdevice,
+                                                                     list[i].
+                                                                     iChannel,
+                                                                     list[i].
+                                                                     iValue,
+                                                                     list[i].
+                                                                     iTimeOut,
+                                                                     list[i].
+                                                                     iFlags);
+
+                                       if (list[i].iErrno) {
+                                               up_read(&me_rwsem);
+                                               karg.errno = list[i].iErrno;
+                                               break;
+                                       }
+                               } else if (list[i].iDir == ME_DIR_INPUT) {
+                                       list[i].iErrno =
+                                           device->
+                                           me_device_io_single_read(device,
+                                                                    filep,
+                                                                    list[i].
+                                                                    iSubdevice,
+                                                                    list[i].
+                                                                    iChannel,
+                                                                    &list[i].
+                                                                    iValue,
+                                                                    list[i].
+                                                                    iTimeOut,
+                                                                    list[i].
+                                                                    iFlags);
+
+                                       if (list[i].iErrno) {
+                                               up_read(&me_rwsem);
+                                               karg.errno = list[i].iErrno;
+                                               break;
+                                       }
+                               } else {
+                                       up_read(&me_rwsem);
+                                       PERROR
+                                           ("Invalid single direction specified.\n");
+                                       list[i].iErrno = ME_ERRNO_INVALID_DIR;
+                                       karg.errno = ME_ERRNO_INVALID_DIR;
+                                       break;
+                               }
+                       }
+
+                       up_read(&me_rwsem);
+               }
+
+               spin_lock(&me_lock);
+
+               me_count--;
+               spin_unlock(&me_lock);
+       }
+
+       err = copy_to_user(arg, &karg, sizeof(me_io_single_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to user space.\n");
+               return -EFAULT;
+       }
+
+       err =
+           copy_to_user(karg.single_list, list,
+                        sizeof(meIOSingle_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy single list to user space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       kfree(list);
+
+       return err;
+}
+
+static int me_io_stream_config(struct file *filep, me_io_stream_config_t * arg)
+{
+       int err;
+       int k = 0;
+
+       struct list_head *pos;
+       me_device_t *device;
+       me_io_stream_config_t karg;
+       meIOStreamConfig_t *list;
+
+       PDEBUG("executed.\n");
+
+       err = copy_from_user(&karg, arg, sizeof(me_io_stream_config_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to kernel space.\n");
+               return -EFAULT;
+       }
+
+       list = kmalloc(sizeof(meIOStreamConfig_t) * karg.count, GFP_KERNEL);
+
+       if (!list) {
+               PERROR("Can't get buffer for config list.\n");
+               return -ENOMEM;
+       }
+
+       err =
+           copy_from_user(list, karg.config_list,
+                          sizeof(meIOStreamConfig_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy config list to kernel space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       spin_lock(&me_lock);
+
+       if ((me_filep != NULL) && (me_filep != filep)) {
+               spin_unlock(&me_lock);
+               PERROR("Driver System is logged by another process.\n");
+               karg.errno = ME_ERRNO_LOCKED;
+       } else {
+               me_count++;
+               spin_unlock(&me_lock);
+
+               down_read(&me_rwsem);
+
+               list_for_each(pos, &me_device_list) {
+                       if (k == karg.device) {
+                               device = list_entry(pos, me_device_t, list);
+                               break;
+                       }
+
+                       k++;
+               }
+
+               if (pos == &me_device_list) {
+                       PERROR("Invalid device number specified.\n");
+                       karg.errno = ME_ERRNO_INVALID_DEVICE;
+               } else {
+                       karg.errno =
+                           device->me_device_io_stream_config(device, filep,
+                                                              karg.subdevice,
+                                                              list, karg.count,
+                                                              &karg.trigger,
+                                                              karg.
+                                                              fifo_irq_threshold,
+                                                              karg.flags);
+               }
+
+               up_read(&me_rwsem);
+
+               spin_lock(&me_lock);
+               me_count--;
+               spin_unlock(&me_lock);
+       }
+
+       err = copy_to_user(arg, &karg, sizeof(me_io_stream_config_t));
+
+       if (err) {
+               PERROR("Can't copy back to user space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       kfree(list);
+
+       return err;
+}
+
+static int me_query_number_devices(struct file *filep,
+                                  me_query_number_devices_t * arg)
+{
+       int err;
+       me_query_number_devices_t karg;
+
+       struct list_head *pos;
+
+       PDEBUG("executed.\n");
+
+       karg.number = 0;
+       down_read(&me_rwsem);
+       list_for_each(pos, &me_device_list) {
+               karg.number++;
+       }
+
+       up_read(&me_rwsem);
+
+       karg.errno = ME_ERRNO_SUCCESS;
+
+       err = copy_to_user(arg, &karg, sizeof(me_query_number_devices_t));
+
+       if (err) {
+               PERROR("Can't copy query back to user space.\n");
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int me_io_stream_stop(struct file *filep, me_io_stream_stop_t * arg)
+{
+       int err;
+       int i, k;
+
+       struct list_head *pos;
+       me_device_t *device;
+       me_io_stream_stop_t karg;
+       meIOStreamStop_t *list;
+
+       PDEBUG("executed.\n");
+
+       err = copy_from_user(&karg, arg, sizeof(me_io_stream_stop_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to kernel space.\n");
+               return -EFAULT;
+       }
+
+       karg.errno = ME_ERRNO_SUCCESS;
+
+       list = kmalloc(sizeof(meIOStreamStop_t) * karg.count, GFP_KERNEL);
+
+       if (!list) {
+               PERROR("Can't get buffer for stop list.\n");
+               return -ENOMEM;
+       }
+
+       err =
+           copy_from_user(list, karg.stop_list,
+                          sizeof(meIOStreamStop_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy stop list to kernel space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       spin_lock(&me_lock);
+
+       if ((me_filep != NULL) && (me_filep != filep)) {
+               spin_unlock(&me_lock);
+               PERROR("Driver System is logged by another process.\n");
+
+               for (i = 0; i < karg.count; i++) {
+                       list[i].iErrno = ME_ERRNO_LOCKED;
+               }
+       } else {
+               me_count++;
+               spin_unlock(&me_lock);
+
+               for (i = 0; i < karg.count; i++) {
+                       k = 0;
+                       down_read(&me_rwsem);
+                       list_for_each(pos, &me_device_list) {
+                               if (k == list[i].iDevice) {
+                                       device =
+                                           list_entry(pos, me_device_t, list);
+                                       break;
+                               }
+
+                               k++;
+                       }
+
+                       if (pos == &me_device_list) {
+                               up_read(&me_rwsem);
+                               PERROR("Invalid device number specified.\n");
+                               list[i].iErrno = ME_ERRNO_INVALID_DEVICE;
+                               karg.errno = ME_ERRNO_INVALID_DEVICE;
+                               break;
+                       } else {
+                               list[i].iErrno =
+                                   device->me_device_io_stream_stop(device,
+                                                                    filep,
+                                                                    list[i].
+                                                                    iSubdevice,
+                                                                    list[i].
+                                                                    iStopMode,
+                                                                    list[i].
+                                                                    iFlags);
+
+                               if (list[i].iErrno) {
+                                       up_read(&me_rwsem);
+                                       karg.errno = list[i].iErrno;
+                                       break;
+                               }
+                       }
+
+                       up_read(&me_rwsem);
+               }
+
+               spin_lock(&me_lock);
+
+               me_count--;
+               spin_unlock(&me_lock);
+       }
+
+       err = copy_to_user(arg, &karg, sizeof(me_io_stream_stop_t));
+
+       if (err) {
+               PERROR("Can't copy arguments to user space.\n");
+               return -EFAULT;
+       }
+
+       err =
+           copy_to_user(karg.stop_list, list,
+                        sizeof(meIOStreamStop_t) * karg.count);
+
+       if (err) {
+               PERROR("Can't copy stop list to user space.\n");
+               kfree(list);
+               return -EFAULT;
+       }
+
+       kfree(list);
+
+       return err;
+}
+
+/*  //me_probe_usb
+static int me_probe_usb(struct usb_interface *interface, const struct usb_device_id *id)
+{
+       //int err;
+       //me_usb_constructor_t *constructor = NULL;
+       me_device_t *n_device = NULL;
+
+       PDEBUG("executed.\n");
+
+       switch (id->idProduct)
+       {
+                       case USB_DEVICE_ID_MEPHISTO_S1:
+                               if((constructor = symbol_get(mephisto_s1_constructor)) == NULL){
+                                       err = request_module(MEPHISTO_S1_NAME);
+                                       if(err){
+                                               PERROR("Error while request for module %s.\n", MEPHISTO_S1_NAME);
+                                               return -ENODEV;
+                                       }
+                                       if((constructor = symbol_get(mephisto_s1_constructor)) == NULL){
+                                               PERROR("Can't get %s driver module constructor.\n", MEPHISTO_S1_NAME);
+                                               return -ENODEV;
+                                       }
+                               }
+
+                               if((n_device = (*constructor)(interface)) == NULL){
+                                       symbol_put(mephisto_s1_constructor);
+                                       PERROR("Can't get device instance of %s driver module.\n", MEPHISTO_S1_NAME);
+                                       return -ENODEV;
+                               }
+
+                               break;
+
+               default:
+                       PERROR("Invalid product id.\n");
+
+                       return -EINVAL;
+       }
+
+       return insert_to_device_list(n_device);
+}
+*/
+
+/*  //me_disconnect_usb
+static void me_disconnect_usb(struct usb_interface *interface)
+{
+
+       struct usb_device *device = interface_to_usbdev(interface);
+       int vendor_id = device->descriptor.idVendor;
+       int device_id = device->descriptor.idProduct;
+       int serial_no;
+
+       sscanf(&device->serial[2], "%x", &serial_no);
+
+       PDEBUG("executed.\n");
+
+       PINFO("Vendor id = 0x%08X\n", vendor_id);
+       PINFO("Device id = 0x%08X\n", device_id);
+       PINFO("Serial Number = 0x%08X\n", serial_no);
+
+       replace_with_dummy(vendor_id, device_id, serial_no);
+}
+*/
+
+static int me_ioctl(struct inode *inodep,
+                   struct file *filep, unsigned int service, unsigned long arg)
+{
+
+       PDEBUG("executed.\n");
+
+       if (_IOC_TYPE(service) != MEMAIN_MAGIC) {
+               PERROR("Invalid magic number.\n");
+               return -ENOTTY;
+       }
+
+       PDEBUG("service number: 0x%x.\n", service);
+
+       switch (service) {
+       case ME_IO_IRQ_ENABLE:
+               return me_io_irq_start(filep, (me_io_irq_start_t *) arg);
+
+       case ME_IO_IRQ_WAIT:
+               return me_io_irq_wait(filep, (me_io_irq_wait_t *) arg);
+
+       case ME_IO_IRQ_DISABLE:
+               return me_io_irq_stop(filep, (me_io_irq_stop_t *) arg);
+
+       case ME_IO_RESET_DEVICE:
+               return me_io_reset_device(filep, (me_io_reset_device_t *) arg);
+
+       case ME_IO_RESET_SUBDEVICE:
+               return me_io_reset_subdevice(filep,
+                                            (me_io_reset_subdevice_t *) arg);
+
+       case ME_IO_SINGLE_CONFIG:
+               return me_io_single_config(filep,
+                                          (me_io_single_config_t *) arg);
+
+       case ME_IO_SINGLE:
+               return me_io_single(filep, (me_io_single_t *) arg);
+
+       case ME_IO_STREAM_CONFIG:
+               return me_io_stream_config(filep,
+                                          (me_io_stream_config_t *) arg);
+
+       case ME_IO_STREAM_NEW_VALUES:
+               return me_io_stream_new_values(filep,
+                                              (me_io_stream_new_values_t *)
+                                              arg);
+
+       case ME_IO_STREAM_READ:
+               return me_io_stream_read(filep, (me_io_stream_read_t *) arg);
+
+       case ME_IO_STREAM_START:
+               return me_io_stream_start(filep, (me_io_stream_start_t *) arg);
+
+       case ME_IO_STREAM_STATUS:
+               return me_io_stream_status(filep,
+                                          (me_io_stream_status_t *) arg);
+
+       case ME_IO_STREAM_STOP:
+               return me_io_stream_stop(filep, (me_io_stream_stop_t *) arg);
+
+       case ME_IO_STREAM_WRITE:
+               return me_io_stream_write(filep, (me_io_stream_write_t *) arg);
+
+       case ME_LOCK_DRIVER:
+               return me_lock_driver(filep, (me_lock_driver_t *) arg);
+
+       case ME_LOCK_DEVICE:
+               return me_lock_device(filep, (me_lock_device_t *) arg);
+
+       case ME_LOCK_SUBDEVICE:
+               return me_lock_subdevice(filep, (me_lock_subdevice_t *) arg);
+
+       case ME_QUERY_INFO_DEVICE:
+               return me_query_info_device(filep,
+                                           (me_query_info_device_t *) arg);
+
+       case ME_QUERY_DESCRIPTION_DEVICE:
+               return me_query_description_device(filep,
+                                                  (me_query_description_device_t
+                                                   *) arg);
+
+       case ME_QUERY_NAME_DEVICE:
+               return me_query_name_device(filep,
+                                           (me_query_name_device_t *) arg);
+
+       case ME_QUERY_NAME_DEVICE_DRIVER:
+               return me_query_name_device_driver(filep,
+                                                  (me_query_name_device_driver_t
+                                                   *) arg);
+
+       case ME_QUERY_NUMBER_DEVICES:
+               return me_query_number_devices(filep,
+                                              (me_query_number_devices_t *)
+                                              arg);
+
+       case ME_QUERY_NUMBER_SUBDEVICES:
+               return me_query_number_subdevices(filep,
+                                                 (me_query_number_subdevices_t
+                                                  *) arg);
+
+       case ME_QUERY_NUMBER_CHANNELS:
+               return me_query_number_channels(filep,
+                                               (me_query_number_channels_t *)
+                                               arg);
+
+       case ME_QUERY_NUMBER_RANGES:
+               return me_query_number_ranges(filep,
+                                             (me_query_number_ranges_t *) arg);
+
+       case ME_QUERY_RANGE_BY_MIN_MAX:
+               return me_query_range_by_min_max(filep,
+                                                (me_query_range_by_min_max_t *)
+                                                arg);
+
+       case ME_QUERY_RANGE_INFO:
+               return me_query_range_info(filep,
+                                          (me_query_range_info_t *) arg);
+
+       case ME_QUERY_SUBDEVICE_BY_TYPE:
+               return me_query_subdevice_by_type(filep,
+                                                 (me_query_subdevice_by_type_t
+                                                  *) arg);
+
+       case ME_QUERY_SUBDEVICE_TYPE:
+               return me_query_subdevice_type(filep,
+                                              (me_query_subdevice_type_t *)
+                                              arg);
+
+       case ME_QUERY_SUBDEVICE_CAPS:
+               return me_query_subdevice_caps(filep,
+                                              (me_query_subdevice_caps_t *)
+                                              arg);
+
+       case ME_QUERY_SUBDEVICE_CAPS_ARGS:
+               return me_query_subdevice_caps_args(filep,
+                                                   (me_query_subdevice_caps_args_t
+                                                    *) arg);
+
+       case ME_QUERY_TIMER:
+               return me_query_timer(filep, (me_query_timer_t *) arg);
+
+       case ME_QUERY_VERSION_MAIN_DRIVER:
+               return me_query_version_main_driver(filep,
+                                                   (me_query_version_main_driver_t
+                                                    *) arg);
+
+       case ME_QUERY_VERSION_DEVICE_DRIVER:
+               return me_query_version_device_driver(filep,
+                                                     (me_query_version_device_driver_t
+                                                      *) arg);
+
+       case ME_CONFIG_LOAD:
+               return me_config_load(filep, (me_config_load_t *) arg);
+       }
+
+       PERROR("Invalid ioctl number.\n");
+       return -ENOTTY;
+}
+
+// Init and exit of module.
+static int memain_init(void)
+{
+       int result = 0;
+       dev_t dev = MKDEV(major, 0);
+
+       PDEBUG("executed.\n");
+
+       // Register pci driver. This will return 0 if the PCI subsystem is not available.
+       result = pci_register_driver(&me_pci_driver);
+
+       if (result < 0) {
+               PERROR("Can't register pci driver.\n");
+               goto INIT_ERROR_1;
+       }
+
+/*
+       // Register usb driver. This will return -ENODEV if no USB subsystem is available.
+       result = usb_register(&me_usb_driver);
+
+       if (result)
+       {
+               if (result == -ENODEV)
+               {
+                       PERROR("No USB subsystem available.\n");
+               }
+               else
+               {
+                       PERROR("Can't register usb driver.\n");
+                       goto INIT_ERROR_2;
+               }
+       }
+*/
+       // Register the character device.
+       if (major) {
+               result = register_chrdev_region(dev, 1, MEMAIN_NAME);
+       } else {
+               result = alloc_chrdev_region(&dev, 0, 1, MEMAIN_NAME);
+               major = MAJOR(dev);
+       }
+
+       if (result < 0) {
+               PERROR("Can't get major driver no.\n");
+               goto INIT_ERROR_3;
+       }
+
+       cdevp = cdev_alloc();
+
+       if (!cdevp) {
+               PERROR("Can't get character device structure.\n");
+               result = -ENOMEM;
+               goto INIT_ERROR_4;
+       }
+
+       cdevp->ops = &me_file_operations;
+
+       cdevp->owner = THIS_MODULE;
+
+       result = cdev_add(cdevp, dev, 1);
+
+       if (result < 0) {
+               PERROR("Cannot add character device structure.\n");
+               goto INIT_ERROR_5;
+       }
+
+       return 0;
+
+      INIT_ERROR_5:
+       cdev_del(cdevp);
+
+      INIT_ERROR_4:
+       unregister_chrdev_region(dev, 1);
+
+      INIT_ERROR_3:
+//      usb_deregister(&me_usb_driver);
+
+//INIT_ERROR_2:
+       pci_unregister_driver(&me_pci_driver);
+       clear_device_list();
+
+      INIT_ERROR_1:
+       return result;
+}
+
+static void __exit memain_exit(void)
+{
+       dev_t dev = MKDEV(major, 0);
+
+       PDEBUG("executed.\n");
+
+       cdev_del(cdevp);
+       unregister_chrdev_region(dev, 1);
+       pci_unregister_driver(&me_pci_driver);
+//      usb_deregister(&me_usb_driver);
+       clear_device_list();
+}
+
+module_init(memain_init);
+module_exit(memain_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR
+    ("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
+MODULE_DESCRIPTION("Central module for Meilhaus Driver System.");
+MODULE_SUPPORTED_DEVICE("Meilhaus PCI/cPCI boards.");
+MODULE_LICENSE("GPL");
+
+#ifdef BOSCH
+// Export the flag for the BOSCH firmware.
+EXPORT_SYMBOL(me_bosch_fw);
+#endif // BOSCH
diff --git a/drivers/staging/meilhaus/memain.h b/drivers/staging/meilhaus/memain.h
new file mode 100644 (file)
index 0000000..7616ff7
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : memain.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _MEMAIN_H_
+#define _MEMAIN_H_
+
+#include "meinternal.h"
+
+#include "meids.h"
+#include "medebug.h"
+
+#include "medevice.h"
+/*#include "me1000_device.h"
+#include "me1400_device.h"
+#include "me1600_device.h"*/
+#include "me4600_device.h"
+/*#include "me6000_device.h"
+#include "me0600_device.h"
+#include "me8100_device.h"
+#include "me8200_device.h"
+#include "me0900_device.h"*/
+#include "medummy.h"
+
+#ifdef __KERNEL__
+
+/*=============================================================================
+  PCI device table.
+  This is used by modprobe to translate PCI IDs to drivers.
+  ===========================================================================*/
+
+static struct pci_device_id me_pci_table[] __devinitdata = {
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1000, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1000_A, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1000_B, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1400, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME140A, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME140B, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME14E0, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME14EA, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME14EB, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME140C, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME140D, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1600_4U, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1600_8U, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1600_12U, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1600_16U, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I,
+        PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4610, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4650, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4660, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4660I, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670I, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670S, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670IS, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680I, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680S, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680IS, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6004, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6008, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME600F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6014, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6018, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME601F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6034, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6038, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME603F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6104, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6108, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME610F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6114, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6118, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME611F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6134, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6138, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME613F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6044, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6048, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME604F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6054, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6058, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME605F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6074, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6078, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME607F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6144, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6148, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME614F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6154, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6158, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME615F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6174, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6178, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME617F, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6259, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME6359, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME0630, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME8100_A, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME8100_B, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME8200_A, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME8200_B, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME0940, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME0950, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+       {PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME0960, PCI_ANY_ID,
+        PCI_ANY_ID, 0, 0, 0},
+
+       {0}
+};
+
+MODULE_DEVICE_TABLE(pci, me_pci_table);
+
+/*=============================================================================
+  USB device table.
+  This is used by modprobe to translate USB IDs to drivers.
+  ===========================================================================*/
+/*
+static struct usb_device_id me_usb_table[] __devinitdata = {
+       { USB_DEVICE(USB_VENDOR_ID_MEPHISTO_S1, USB_DEVICE_ID_MEPHISTO_S1) },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE (usb, me_usb_table);
+*/
+
+/*=============================================================================
+  Templates
+  ===========================================================================*/
+
+#define ME_LOCK_MULTIPLEX_TEMPLATE(NAME, TYPE, CALL, DEV_CALL, ARGS)   \
+static int CALL(struct file *filep, TYPE *arg){        \
+       int err = 0; \
+       int k = 0; \
+       struct list_head *pos; \
+       me_device_t *device; \
+       TYPE karg; \
+       \
+       PDEBUG("executed.\n"); \
+       \
+       err = copy_from_user(&karg, arg, sizeof(TYPE)); \
+       if(err){ \
+               PERROR("Can't copy arguments to kernel space\n"); \
+               return -EFAULT; \
+       } \
+       \
+       down_read(&me_rwsem);   \
+       \
+       list_for_each(pos, &me_device_list){    \
+               if(k == karg.device){   \
+                       device = list_entry(pos, me_device_t, list);    \
+                               break;  \
+               }       \
+               k++;    \
+       }       \
+       \
+       if(pos == &me_device_list){ \
+               PERROR("Invalid device number specified\n"); \
+               karg.errno = ME_ERRNO_INVALID_DEVICE; \
+       } \
+       else{ \
+               spin_lock(&me_lock);    \
+               if((me_filep != NULL) && (me_filep != filep)){  \
+                       spin_unlock(&me_lock);  \
+                       PERROR("Resource is locked by another process\n");      \
+                       if(karg.lock == ME_LOCK_SET)    \
+                               karg.errno = ME_ERRNO_LOCKED;   \
+                       else if(karg.lock == ME_LOCK_RELEASE)   \
+                               karg.errno = ME_ERRNO_SUCCESS;  \
+                       else{   \
+                               PERROR("Invalid lock specified\n");     \
+                               karg.errno = ME_ERRNO_INVALID_LOCK;     \
+                       }\
+               }       \
+               else {  \
+                       me_count++;     \
+                       spin_unlock(&me_lock);  \
+                       \
+                       karg.errno = device->DEV_CALL ARGS;     \
+                       \
+                       spin_lock(&me_lock);    \
+                       me_count--;     \
+                       spin_unlock(&me_lock);  \
+               }       \
+       } \
+       \
+       up_read(&me_rwsem);     \
+       \
+       err = copy_to_user(arg, &karg, sizeof(TYPE)); \
+       if(err){ \
+               PERROR("Can't copy arguments back to user space\n"); \
+               return -EFAULT; \
+       } \
+       \
+       return ME_ERRNO_SUCCESS; \
+}
+
+#define ME_IO_MULTIPLEX_TEMPLATE(NAME, TYPE, CALL, DEV_CALL, ARGS)     \
+static int CALL(struct file *filep, TYPE *arg){        \
+       int err = 0; \
+       int k = 0; \
+       struct list_head *pos; \
+       me_device_t *device; \
+       TYPE karg; \
+       \
+       PDEBUG("executed.\n"); \
+       \
+       err = copy_from_user(&karg, arg, sizeof(TYPE)); \
+       if(err){ \
+               PERROR("Can't copy arguments to kernel space\n"); \
+               return -EFAULT; \
+       } \
+       \
+       down_read(&me_rwsem);   \
+       \
+       list_for_each(pos, &me_device_list){    \
+               if(k == karg.device){   \
+                       device = list_entry(pos, me_device_t, list);    \
+                               break;  \
+               }       \
+               k++;    \
+       }       \
+       \
+       if(pos == &me_device_list){ \
+               PERROR("Invalid device number specified\n"); \
+               karg.errno = ME_ERRNO_INVALID_DEVICE; \
+       } \
+       else{ \
+               spin_lock(&me_lock);    \
+               if((me_filep != NULL) && (me_filep != filep)){  \
+                       spin_unlock(&me_lock);  \
+                       PERROR("Resource is locked by another process\n");      \
+                       karg.errno = ME_ERRNO_LOCKED;   \
+               }       \
+               else {  \
+                       me_count++;     \
+                       spin_unlock(&me_lock);  \
+                       \
+                       karg.errno = device->DEV_CALL ARGS;     \
+                       \
+                       spin_lock(&me_lock);    \
+                       me_count--;     \
+                       spin_unlock(&me_lock);  \
+               }       \
+       } \
+       \
+       up_read(&me_rwsem);     \
+       \
+       err = copy_to_user(arg, &karg, sizeof(TYPE)); \
+       if(err){ \
+               PERROR("Can't copy arguments back to user space\n"); \
+               return -EFAULT; \
+       } \
+ \
+       return ME_ERRNO_SUCCESS; \
+}
+
+#define ME_QUERY_MULTIPLEX_STR_TEMPLATE(NAME, TYPE, CALL, DEV_CALL, ARGS)      \
+static int CALL(struct file *filep, TYPE *arg){        \
+       int err = 0;    \
+       int k = 0;      \
+       struct list_head *pos;  \
+       me_device_t *device;    \
+       char *msg = NULL;       \
+       TYPE karg;      \
+       \
+       PDEBUG("executed.\n"); \
+       \
+       err = copy_from_user(&karg, arg, sizeof(TYPE)); \
+       if(err){        \
+               PERROR("Can't copy arguments to kernel space\n");       \
+               return -EFAULT; \
+       }       \
+       \
+       down_read(&me_rwsem);   \
+       \
+       list_for_each(pos, &me_device_list){    \
+               if(k == karg.device){   \
+                       device = list_entry(pos, me_device_t, list);    \
+                               break;  \
+               }       \
+               k++;    \
+       }       \
+       \
+       if(pos == &me_device_list){     \
+               PERROR("Invalid device number specified\n");    \
+               karg.errno = ME_ERRNO_INVALID_DEVICE;   \
+       }       \
+       else{   \
+               karg.errno = device->DEV_CALL ARGS;     \
+               if(!karg.errno){        \
+                       if((strlen(msg) + 1) > karg.count){     \
+                               PERROR("User buffer for device name is to little\n");   \
+                               karg.errno = ME_ERRNO_USER_BUFFER_SIZE; \
+                       }       \
+                       else{   \
+                               err = copy_to_user(karg.name, msg, strlen(msg) + 1);    \
+                               if(err){        \
+                                       PERROR("Can't copy device name to user space\n");       \
+                                       return -EFAULT; \
+                               }       \
+                       }       \
+               }       \
+       }       \
+       \
+       up_read(&me_rwsem);     \
+       \
+       err = copy_to_user(arg, &karg, sizeof(TYPE));   \
+       if(err){        \
+               PERROR("Can't copy query back to user space\n");        \
+               return -EFAULT; \
+       }       \
+       \
+       return ME_ERRNO_SUCCESS;        \
+}
+
+#define ME_QUERY_MULTIPLEX_TEMPLATE(NAME, TYPE, CALL, DEV_CALL, ARGS)  \
+static int CALL(struct file *filep, TYPE *arg){        \
+       int err = 0;    \
+       int k = 0;      \
+       struct list_head *pos;  \
+       me_device_t *device;    \
+       TYPE karg;      \
+               \
+       PDEBUG("executed.\n"); \
+        \
+       err = copy_from_user(&karg, arg, sizeof(TYPE)); \
+       if(err){        \
+               PERROR("Can't copy arguments from user space\n");       \
+               return -EFAULT; \
+       }       \
+       \
+       down_read(&me_rwsem);   \
+       \
+       list_for_each(pos, &me_device_list){    \
+               if(k == karg.device){   \
+                       device = list_entry(pos, me_device_t, list);    \
+                       break;  \
+               }       \
+               k++;    \
+       }       \
+               \
+       if(pos == &me_device_list){     \
+               PERROR("Invalid device number specified\n");    \
+               karg.errno = ME_ERRNO_INVALID_DEVICE;   \
+       }       \
+       else{   \
+               karg.errno = device->DEV_CALL ARGS;     \
+       }       \
+       \
+       up_read(&me_rwsem);     \
+       \
+       err = copy_to_user(arg, &karg, sizeof(TYPE));   \
+       if(err){        \
+               PERROR("Can't copy arguments to user space\n"); \
+               return -EFAULT; \
+       }       \
+               \
+       return ME_ERRNO_SUCCESS;        \
+}
+
+#endif //__KERNEL__
+#endif
diff --git a/drivers/staging/meilhaus/meplx_reg.h b/drivers/staging/meilhaus/meplx_reg.h
new file mode 100644 (file)
index 0000000..1868614
--- /dev/null
@@ -0,0 +1,53 @@
+/**
+ * @file meplx_reg.h
+ *
+ * @brief PLX 9052 PCI bridge register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _MEPLX_REG_H_
+#define _MEPLX_REG_H_
+
+#ifdef __KERNEL__
+
+#define PLX_INTCSR                             0x4C            /**< Interrupt control and status register. */
+#define PLX_INTCSR_LOCAL_INT1_EN               0x01            /**< If set, local interrupt 1 is enabled (r/w). */
+#define PLX_INTCSR_LOCAL_INT1_POL              0x02            /**< If set, local interrupt 1 polarity is active high (r/w). */
+#define PLX_INTCSR_LOCAL_INT1_STATE            0x04            /**< If set, local interrupt 1 is active (r/_). */
+#define PLX_INTCSR_LOCAL_INT2_EN               0x08            /**< If set, local interrupt 2 is enabled (r/w). */
+#define PLX_INTCSR_LOCAL_INT2_POL              0x10            /**< If set, local interrupt 2 polarity is active high (r/w). */
+#define PLX_INTCSR_LOCAL_INT2_STATE            0x20            /**< If set, local interrupt 2 is active  (r/_). */
+#define PLX_INTCSR_PCI_INT_EN                  0x40            /**< If set, PCI interrupt is enabled (r/w). */
+#define PLX_INTCSR_SOFT_INT                    0x80            /**< If set, a software interrupt is generated (r/w). */
+
+#define PLX_ICR                                        0x50            /**< Initialization control register. */
+#define PLX_ICR_BIT_EEPROM_CLOCK_SET           0x01000000
+#define PLX_ICR_BIT_EEPROM_CHIP_SELECT         0x02000000
+#define PLX_ICR_BIT_EEPROM_WRITE               0x04000000
+#define PLX_ICR_BIT_EEPROM_READ                        0x08000000
+#define PLX_ICR_BIT_EEPROM_VALID               0x10000000
+
+#define PLX_ICR_MASK_EEPROM                    0x1F000000
+#define EEPROM_DELAY                           1
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/meslist.c b/drivers/staging/meilhaus/meslist.c
new file mode 100644 (file)
index 0000000..7e8b66c
--- /dev/null
@@ -0,0 +1,173 @@
+/**
+ * @file me_slist.c
+ *
+ * @brief Implements the subdevice list class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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.
+ */
+
+#include "meerror.h"
+#include "medefines.h"
+
+#include "meslist.h"
+#include "medebug.h"
+
+int me_slist_query_number_subdevices(struct me_slist *slist, int *number)
+{
+       PDEBUG_LOCKS("called.\n");
+       *number = slist->n;
+       return ME_ERRNO_SUCCESS;
+}
+
+unsigned int me_slist_get_number_subdevices(struct me_slist *slist)
+{
+       PDEBUG_LOCKS("called.\n");
+       return slist->n;
+}
+
+me_subdevice_t *me_slist_get_subdevice(struct me_slist * slist,
+                                      unsigned int index)
+{
+
+       struct list_head *pos;
+       me_subdevice_t *subdevice = NULL;
+       unsigned int i = 0;
+
+       PDEBUG_LOCKS("called.\n");
+
+       if (index >= slist->n) {
+               PERROR("Index out of range.\n");
+               return NULL;
+       }
+
+       list_for_each(pos, &slist->head) {
+               if (i == index) {
+                       subdevice = list_entry(pos, me_subdevice_t, list);
+                       break;
+               }
+
+               ++i;
+       }
+
+       return subdevice;
+}
+
+int me_slist_get_subdevice_by_type(struct me_slist *slist,
+                                  unsigned int start_subdevice,
+                                  int type, int subtype, int *subdevice)
+{
+       me_subdevice_t *pos;
+       int s_type, s_subtype;
+       unsigned int index = 0;
+
+       PDEBUG_LOCKS("called.\n");
+
+       if (start_subdevice >= slist->n) {
+               PERROR("Start index out of range.\n");
+               return ME_ERRNO_NOMORE_SUBDEVICE_TYPE;
+       }
+
+       list_for_each_entry(pos, &slist->head, list) {
+               if (index < start_subdevice) {  // Go forward to start subdevice.
+                       ++index;
+                       continue;
+               }
+
+               pos->me_subdevice_query_subdevice_type(pos,
+                                                      &s_type, &s_subtype);
+
+               if (subtype == ME_SUBTYPE_ANY) {
+                       if (s_type == type)
+                               break;
+               } else {
+                       if ((s_type == type) && (s_subtype == subtype))
+                               break;
+               }
+
+               ++index;
+       }
+
+       if (index >= slist->n) {
+               return ME_ERRNO_NOMORE_SUBDEVICE_TYPE;
+       }
+
+       *subdevice = index;
+
+       return ME_ERRNO_SUCCESS;
+}
+
+void me_slist_add_subdevice_tail(struct me_slist *slist,
+                                me_subdevice_t * subdevice)
+{
+       PDEBUG_LOCKS("called.\n");
+
+       list_add_tail(&subdevice->list, &slist->head);
+       ++slist->n;
+}
+
+me_subdevice_t *me_slist_del_subdevice_tail(struct me_slist *slist)
+{
+
+       struct list_head *last;
+       me_subdevice_t *subdevice;
+
+       PDEBUG_LOCKS("called.\n");
+
+       if (list_empty(&slist->head))
+               return NULL;
+
+       last = slist->head.prev;
+
+       subdevice = list_entry(last, me_subdevice_t, list);
+
+       list_del(last);
+
+       --slist->n;
+
+       return subdevice;
+}
+
+int me_slist_init(me_slist_t * slist)
+{
+       PDEBUG_LOCKS("called.\n");
+
+       INIT_LIST_HEAD(&slist->head);
+       slist->n = 0;
+       return 0;
+}
+
+void me_slist_deinit(me_slist_t * slist)
+{
+
+       struct list_head *s;
+       me_subdevice_t *subdevice;
+
+       PDEBUG_LOCKS("called.\n");
+
+       while (!list_empty(&slist->head)) {
+               s = slist->head.next;
+               list_del(s);
+               subdevice = list_entry(s, me_subdevice_t, list);
+               subdevice->me_subdevice_destructor(subdevice);
+       }
+
+       slist->n = 0;
+}
diff --git a/drivers/staging/meilhaus/meslist.h b/drivers/staging/meilhaus/meslist.h
new file mode 100644 (file)
index 0000000..d26c896
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * @file me_slist.h
+ *
+ * @brief Provides the subdevice list class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _ME_SLIST_H_
+#define _ME_SLIST_H_
+
+#include <linux/list.h>
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The subdevice list container.
+ */
+typedef struct me_slist {
+       struct list_head head;          /**< The head of the internal list. */
+       unsigned int n;                 /**< The number of subdevices in the list. */
+} me_slist_t;
+
+/**
+ * @brief Queries the number of subdevices currently inside the list.
+ *
+ * @param slist The subdevice list to query.
+ * @param[out] number The number of subdevices of the device.
+ *
+ * @return ME-iDS error code.
+ */
+int me_slist_query_number_subdevices(struct me_slist *slist, int *number);
+
+/**
+ * @brief Returns the number of subdevices currently inside the list.
+ *
+ * @param slist The subdevice list to query.
+ *
+ * @return The number of subdevices in the list.
+ */
+unsigned int me_slist_get_number_subdevices(struct me_slist *slist);
+
+/**
+ * @brief Get a subdevice by index.
+ *
+ * @param slist The subdevice list to query.
+ * @param index The index of the subdevice to get in the list.
+ *
+ * @return The subdevice at index if available.\n
+ *         NULL if the index is out of range.
+ */
+me_subdevice_t *me_slist_get_subdevice(struct me_slist *slist,
+                                      unsigned int index);
+
+/**
+ * @brief Get a subdevice index by type and subtype.
+ *
+ * @param slist The subdevice list to query.
+ * @param start_subdevice The subdevice index at which the start shall begin.
+ * @param type The type of the subdevice to query.
+ * @param subtype The subtype of the subdevice to query.
+ * @param[out] subdevice On success this parameter returns the index of the subdevice matching the requested type.
+ *
+ * @return ME_ERRNO_SUCCESS on success.
+ */
+int me_slist_get_subdevice_by_type(struct me_slist *slist,
+                                  unsigned int start_subdevice,
+                                  int type, int subtype, int *subdevice);
+
+/**
+ * @brief Adds a subdevice to the tail of the list.
+ *
+ * @param slist The subdevice list to add a subdevice to.
+ * @param subdevice The subdevice to add to the list.
+ */
+void me_slist_add_subdevice_tail(struct me_slist *slist,
+                                me_subdevice_t * subdevice);
+
+/**
+ * @brief Removes a subdevice from the tail of the list.
+ *
+ * @param slist The subdevice list.
+ *
+ * @return Pointer to the removed subdeivce.\n
+ *         NULL in cases where the list was empty.
+ */
+me_subdevice_t *me_slist_del_subdevice_tail(struct me_slist *slist);
+
+/**
+ * @brief Initializes a subdevice list structure.
+ *
+ * @param lock The subdevice list structure to initialize.
+ * @return 0 on success.
+ */
+int me_slist_init(me_slist_t * slist);
+
+/**
+ * @brief Deinitializes a subdevice list structure and destructs every subdevice in it.
+ *
+ * @param slist The subdevice list structure to deinitialize.
+ * @return 0 on success.
+ */
+void me_slist_deinit(me_slist_t * slist);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/meslock.c b/drivers/staging/meilhaus/meslock.c
new file mode 100644 (file)
index 0000000..5230b89
--- /dev/null
@@ -0,0 +1,136 @@
+/**
+ * @file meslock.c
+ *
+ * @brief Implements the subdevice lock class.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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.
+ */
+
+#include <linux/spinlock.h>
+
+#include "medefines.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "meslock.h"
+
+int me_slock_enter(struct me_slock *slock, struct file *filep)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       spin_lock(&slock->spin_lock);
+
+       if ((slock->filep) != NULL && (slock->filep != filep)) {
+               PERROR("Subdevice is locked by another process.\n");
+               spin_unlock(&slock->spin_lock);
+               return ME_ERRNO_LOCKED;
+       }
+
+       slock->count++;
+
+       spin_unlock(&slock->spin_lock);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+int me_slock_exit(struct me_slock *slock, struct file *filep)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       spin_lock(&slock->spin_lock);
+       slock->count--;
+       spin_unlock(&slock->spin_lock);
+
+       return ME_ERRNO_SUCCESS;
+}
+
+int me_slock_lock(struct me_slock *slock, struct file *filep, int lock)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       switch (lock) {
+
+       case ME_LOCK_RELEASE:
+               spin_lock(&slock->spin_lock);
+
+               if (slock->filep == filep)
+                       slock->filep = NULL;
+
+               spin_unlock(&slock->spin_lock);
+
+               break;
+
+       case ME_LOCK_SET:
+               spin_lock(&slock->spin_lock);
+
+               if (slock->count) {
+                       spin_unlock(&slock->spin_lock);
+                       PERROR("Subdevice is used by another process.\n");
+                       return ME_ERRNO_USED;
+               } else if (slock->filep == NULL)
+                       slock->filep = filep;
+               else if (slock->filep != filep) {
+                       spin_unlock(&slock->spin_lock);
+                       PERROR("Subdevice is locked by another process.\n");
+                       return ME_ERRNO_LOCKED;
+               }
+
+               spin_unlock(&slock->spin_lock);
+
+               break;
+
+       case ME_LOCK_CHECK:
+               spin_lock(&slock->spin_lock);
+
+               if (slock->count) {
+                       spin_unlock(&slock->spin_lock);
+                       return ME_ERRNO_USED;
+               } else if ((slock->filep != NULL) && (slock->filep != filep)) {
+                       spin_unlock(&slock->spin_lock);
+                       return ME_ERRNO_LOCKED;
+               }
+
+               spin_unlock(&slock->spin_lock);
+
+               break;
+
+       default:
+               break;
+       }
+
+       return ME_ERRNO_SUCCESS;
+}
+
+void me_slock_deinit(struct me_slock *slock)
+{
+       PDEBUG_LOCKS("executed.\n");
+}
+
+int me_slock_init(me_slock_t * slock)
+{
+       PDEBUG_LOCKS("executed.\n");
+
+       slock->filep = NULL;
+       slock->count = 0;
+       spin_lock_init(&slock->spin_lock);
+
+       return 0;
+}
diff --git a/drivers/staging/meilhaus/meslock.h b/drivers/staging/meilhaus/meslock.h
new file mode 100644 (file)
index 0000000..f42b25c
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+ * @file meslock.h
+ *
+ * @brief Provides the subdevice lock class.
+ * @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _MESLOCK_H_
+#define _MESLOCK_H_
+
+#include <linux/spinlock.h>
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The subdevice lock class.
+ */
+typedef struct me_slock {
+       struct file *filep;             /**< Pointer to file structure holding the subdevice. */
+       int count;                      /**< Number of tasks which are inside the subdevice. */
+       spinlock_t spin_lock;           /**< Spin lock protecting the attributes from concurrent access. */
+} me_slock_t;
+
+/**
+ * @brief Tries to enter a subdevice.
+ *
+ * @param slock The subdevice lock instance.
+ * @param filep The file structure identifying the calling process.
+ *
+ * @return 0 on success.
+ */
+int me_slock_enter(struct me_slock *slock, struct file *filep);
+
+/**
+ * @brief Exits a subdevice.
+ *
+ * @param slock The subdevice lock instance.
+ * @param filep The file structure identifying the calling process.
+ *
+ * @return 0 on success.
+ */
+int me_slock_exit(struct me_slock *slock, struct file *filep);
+
+/**
+ * @brief Tries to perform a locking action on a subdevice.
+ *
+ * @param slock The subdevice lock instance.
+ * @param filep The file structure identifying the calling process.
+ * @param The action to be done.
+ *
+ * @return 0 on success.
+ */
+int me_slock_lock(struct me_slock *slock, struct file *filep, int lock);
+
+/**
+ * @brief Initializes a lock structure.
+ *
+ * @param slock The lock structure to initialize.
+ * @return 0 on success.
+ */
+int me_slock_init(me_slock_t * slock);
+
+/**
+ * @brief Deinitializes a lock structure.
+ *
+ * @param slock The lock structure to deinitialize.
+ * @return 0 on success.
+ */
+void me_slock_deinit(me_slock_t * slock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/mesubdevice.c b/drivers/staging/meilhaus/mesubdevice.c
new file mode 100644 (file)
index 0000000..98d4f1f
--- /dev/null
@@ -0,0 +1,317 @@
+/**
+ * @file mesubdevice.c
+ *
+ * @brief Subdevice base class implemention.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#include <linux/slab.h>
+
+#include "medefines.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "mesubdevice.h"
+
+static int me_subdevice_io_irq_start(struct me_subdevice *subdevice,
+                                    struct file *filep,
+                                    int channel,
+                                    int irq_source,
+                                    int irq_edge, int irq_arg, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_irq_wait(struct me_subdevice *subdevice,
+                                   struct file *filep,
+                                   int channel,
+                                   int *irq_count,
+                                   int *value, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_irq_stop(struct me_subdevice *subdevice,
+                                   struct file *filep, int channel, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_reset_subdevice(struct me_subdevice *subdevice,
+                                          struct file *filep, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_single_config(struct me_subdevice *subdevice,
+                                        struct file *filep,
+                                        int channel,
+                                        int single_config,
+                                        int ref,
+                                        int trig_chan,
+                                        int trig_type,
+                                        int trig_edge, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_single_read(struct me_subdevice *subdevice,
+                                      struct file *filep,
+                                      int channel,
+                                      int *value, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_single_write(struct me_subdevice *subdevice,
+                                       struct file *filep,
+                                       int channel,
+                                       int value, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_config(struct me_subdevice *subdevice,
+                                        struct file *filep,
+                                        meIOStreamConfig_t * config_list,
+                                        int count,
+                                        meIOStreamTrigger_t * trigger,
+                                        int fifo_irq_threshold, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_new_values(struct me_subdevice *subdevice,
+                                            struct file *filep,
+                                            int time_out,
+                                            int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_read(struct me_subdevice *subdevice,
+                                      struct file *filep,
+                                      int read_mode,
+                                      int *values, int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_start(struct me_subdevice *subdevice,
+                                       struct file *filep,
+                                       int start_mode, int time_out, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_status(struct me_subdevice *subdevice,
+                                        struct file *filep,
+                                        int wait,
+                                        int *status, int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_stop(struct me_subdevice *subdevice,
+                                      struct file *filep,
+                                      int stop_mode, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_io_stream_write(struct me_subdevice *subdevice,
+                                       struct file *filep,
+                                       int write_mode,
+                                       int *values, int *count, int flags)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_lock_subdevice(me_subdevice_t * subdevice,
+                                      struct file *filep, int lock, int flags)
+{
+       PDEBUG("executed.\n");
+       return me_slock_lock(&subdevice->lock, filep, lock);
+}
+
+static int me_subdevice_query_number_channels(struct me_subdevice *subdevice,
+                                             int *number)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_query_number_ranges(struct me_subdevice *subdevice,
+                                           int unit, int *count)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_query_range_by_min_max(struct me_subdevice *subdevice,
+                                              int unit,
+                                              int *min,
+                                              int *max,
+                                              int *maxdata, int *range)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_query_range_info(struct me_subdevice *subdevice,
+                                        int range,
+                                        int *unit,
+                                        int *min, int *max, int *maxdata)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_query_subdevice_type(struct me_subdevice *subdevice,
+                                            int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_query_subdevice_caps(struct me_subdevice *subdevice,
+                                            int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int me_subdevice_query_subdevice_caps_args(struct me_subdevice
+                                                 *subdevice, int cap,
+                                                 int *args, int count)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_query_timer(struct me_subdevice *subdevice,
+                                   int timer,
+                                   int *base_frequency,
+                                   long long *min_ticks, long long *max_ticks)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_NOT_SUPPORTED;
+}
+
+static int me_subdevice_config_load(struct me_subdevice *subdevice,
+                                   me_cfg_device_entry_t * config)
+{
+       PDEBUG("executed.\n");
+       return ME_ERRNO_SUCCESS;
+}
+
+static void me_subdevice_destructor(struct me_subdevice *subdevice)
+{
+       PDEBUG("executed.\n");
+       me_subdevice_deinit(subdevice);
+       kfree(subdevice);
+}
+
+int me_subdevice_init(me_subdevice_t * subdevice)
+{
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Init list head */
+       INIT_LIST_HEAD(&subdevice->list);
+
+       /* Initialize the subdevice lock instance */
+
+       err = me_slock_init(&subdevice->lock);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice lock instance.\n");
+               return 1;
+       }
+
+       /* Subdevice base class methods */
+       subdevice->me_subdevice_io_irq_start = me_subdevice_io_irq_start;
+       subdevice->me_subdevice_io_irq_wait = me_subdevice_io_irq_wait;
+       subdevice->me_subdevice_io_irq_stop = me_subdevice_io_irq_stop;
+       subdevice->me_subdevice_io_reset_subdevice =
+           me_subdevice_io_reset_subdevice;
+       subdevice->me_subdevice_io_single_config =
+           me_subdevice_io_single_config;
+       subdevice->me_subdevice_io_single_read = me_subdevice_io_single_read;
+       subdevice->me_subdevice_io_single_write = me_subdevice_io_single_write;
+       subdevice->me_subdevice_io_stream_config =
+           me_subdevice_io_stream_config;
+       subdevice->me_subdevice_io_stream_new_values =
+           me_subdevice_io_stream_new_values;
+       subdevice->me_subdevice_io_stream_read = me_subdevice_io_stream_read;
+       subdevice->me_subdevice_io_stream_start = me_subdevice_io_stream_start;
+       subdevice->me_subdevice_io_stream_status =
+           me_subdevice_io_stream_status;
+       subdevice->me_subdevice_io_stream_stop = me_subdevice_io_stream_stop;
+       subdevice->me_subdevice_io_stream_write = me_subdevice_io_stream_write;
+       subdevice->me_subdevice_lock_subdevice = me_subdevice_lock_subdevice;
+       subdevice->me_subdevice_query_number_channels =
+           me_subdevice_query_number_channels;
+       subdevice->me_subdevice_query_number_ranges =
+           me_subdevice_query_number_ranges;
+       subdevice->me_subdevice_query_range_by_min_max =
+           me_subdevice_query_range_by_min_max;
+       subdevice->me_subdevice_query_range_info =
+           me_subdevice_query_range_info;
+       subdevice->me_subdevice_query_subdevice_type =
+           me_subdevice_query_subdevice_type;
+       subdevice->me_subdevice_query_subdevice_caps =
+           me_subdevice_query_subdevice_caps;
+       subdevice->me_subdevice_query_subdevice_caps_args =
+           me_subdevice_query_subdevice_caps_args;
+       subdevice->me_subdevice_query_timer = me_subdevice_query_timer;
+       subdevice->me_subdevice_config_load = me_subdevice_config_load;
+       subdevice->me_subdevice_destructor = me_subdevice_destructor;
+
+       return 0;
+}
+
+void me_subdevice_deinit(me_subdevice_t * subdevice)
+{
+       PDEBUG("executed.\n");
+       me_subdevice_io_reset_subdevice(subdevice, NULL,
+                                       ME_IO_RESET_SUBDEVICE_NO_FLAGS);
+       me_slock_deinit(&subdevice->lock);
+}
diff --git a/drivers/staging/meilhaus/mesubdevice.h b/drivers/staging/meilhaus/mesubdevice.h
new file mode 100644 (file)
index 0000000..19ec2b5
--- /dev/null
@@ -0,0 +1,197 @@
+/**
+ * @file mesubdevice.h
+ *
+ * @brief Provides the subdevice base class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+#ifndef _MESUBDEVICE_H_
+#define _MESUBDEVICE_H_
+
+#include <linux/list.h>
+
+#include "metypes.h"
+#include "meioctl.h"
+#include "meslock.h"
+
+# include <linux/workqueue.h>
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Macro used to enter a subdevice.
+ */
+#define ME_SUBDEVICE_ENTER     \
+{ \
+       int err; \
+       err = me_slock_enter(&instance->base.lock, filep); \
+       if(err){ \
+               PERROR("Cannot enter subdevice.\n"); \
+               return err; \
+       } \
+}
+
+/**
+ * @brief Macro used to exit a subdevice.
+ */
+#define ME_SUBDEVICE_EXIT      \
+{\
+       int err; \
+       err = me_slock_exit(&instance->base.lock, filep); \
+       if(err){ \
+               PERROR("Cannot exit subdevice.\n"); \
+               return err; \
+       } \
+}
+
+/**
+ * @brief The subdevice base class.
+ */
+typedef struct me_subdevice {
+       /* Attributes */
+       struct list_head list;          /**< Enables the subdevice to be added to a dynamic list. */
+       me_slock_t lock;                /**< Used by user application in order to lock the subdevice for exclusive usage. */
+
+       /* Methods */
+       int (*me_subdevice_io_irq_start) (struct me_subdevice * subdevice,
+                                         struct file * filep,
+                                         int channel,
+                                         int irq_source,
+                                         int irq_edge, int irq_arg, int flags);
+
+       int (*me_subdevice_io_irq_wait) (struct me_subdevice * subdevice,
+                                        struct file * filep,
+                                        int channel,
+                                        int *irq_count,
+                                        int *value, int time_out, int flags);
+
+       int (*me_subdevice_io_irq_stop) (struct me_subdevice * subdevice,
+                                        struct file * filep,
+                                        int channel, int flags);
+
+       int (*me_subdevice_io_reset_subdevice) (struct me_subdevice * subdevice,
+                                               struct file * filep, int flags);
+
+       int (*me_subdevice_io_single_config) (struct me_subdevice * subdevice,
+                                             struct file * filep,
+                                             int channel,
+                                             int single_config,
+                                             int ref,
+                                             int trig_chan,
+                                             int trig_type,
+                                             int trig_edge, int flags);
+
+       int (*me_subdevice_io_single_read) (struct me_subdevice * subdevice,
+                                           struct file * filep,
+                                           int channel,
+                                           int *value,
+                                           int time_out, int flags);
+
+       int (*me_subdevice_io_single_write) (struct me_subdevice * subdevice,
+                                            struct file * filep,
+                                            int channel,
+                                            int value,
+                                            int time_out, int flags);
+
+       int (*me_subdevice_io_stream_config) (struct me_subdevice * subdevice,
+                                             struct file * filep,
+                                             meIOStreamConfig_t * config_list,
+                                             int count,
+                                             meIOStreamTrigger_t * trigger,
+                                             int fifo_irq_threshold,
+                                             int flags);
+
+       int (*me_subdevice_io_stream_new_values) (struct me_subdevice *
+                                                 subdevice,
+                                                 struct file * filep,
+                                                 int time_out, int *count,
+                                                 int flags);
+
+       int (*me_subdevice_io_stream_read) (struct me_subdevice * subdevice,
+                                           struct file * filep,
+                                           int read_mode,
+                                           int *values, int *count, int flags);
+
+       int (*me_subdevice_io_stream_start) (struct me_subdevice * subdevice,
+                                            struct file * filep,
+                                            int start_mode,
+                                            int time_out, int flags);
+
+       int (*me_subdevice_io_stream_status) (struct me_subdevice * subdevice,
+                                             struct file * filep,
+                                             int wait,
+                                             int *status,
+                                             int *count, int flags);
+
+       int (*me_subdevice_io_stream_stop) (struct me_subdevice * subdevice,
+                                           struct file * filep,
+                                           int stop_mode, int flags);
+
+       int (*me_subdevice_io_stream_write) (struct me_subdevice * subdevice,
+                                            struct file * filep,
+                                            int write_mode,
+                                            int *values,
+                                            int *count, int flags);
+
+       int (*me_subdevice_lock_subdevice) (struct me_subdevice * subdevice,
+                                           struct file * filep,
+                                           int lock, int flags);
+
+       int (*me_subdevice_query_number_channels) (struct me_subdevice *
+                                                  subdevice, int *number);
+
+       int (*me_subdevice_query_number_ranges) (struct me_subdevice *
+                                                subdevice, int unit,
+                                                int *count);
+
+       int (*me_subdevice_query_range_by_min_max) (struct me_subdevice *
+                                                   subdevice, int unit,
+                                                   int *min, int *max,
+                                                   int *maxdata, int *range);
+
+       int (*me_subdevice_query_range_info) (struct me_subdevice * subdevice,
+                                             int range,
+                                             int *unit,
+                                             int *min, int *max, int *maxdata);
+
+       int (*me_subdevice_query_subdevice_type) (struct me_subdevice *
+                                                 subdevice, int *type,
+                                                 int *subtype);
+
+       int (*me_subdevice_query_subdevice_caps) (struct me_subdevice *
+                                                 subdevice, int *caps);
+
+       int (*me_subdevice_query_subdevice_caps_args) (struct me_subdevice *
+                                                      subdevice, int cap,
+                                                      int *args, int count);
+
+       int (*me_subdevice_query_timer) (struct me_subdevice * subdevice,
+                                        int timer,
+                                        int *base_frequency,
+                                        long long *min_ticks,
+                                        long long *max_ticks);
+
+       int (*me_subdevice_config_load) (struct me_subdevice * subdevice,
+                                        me_cfg_device_entry_t * config);
+
+       void (*me_subdevice_destructor) (struct me_subdevice * subdevice);
+} me_subdevice_t;
+
+/**
+ * @brief Initializes a subdevice structure.
+ *
+ * @param subdevice The subdevice structure to initialize.
+ * @return 0 on success.
+ */
+int me_subdevice_init(me_subdevice_t * subdevice);
+
+/**
+ * @brief Deinitializes a subdevice structure.
+ *
+ * @param subdevice The subdevice structure to initialize.
+ */
+void me_subdevice_deinit(me_subdevice_t * subdevice);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/metempl_device.c b/drivers/staging/meilhaus/metempl_device.c
new file mode 100644 (file)
index 0000000..e48632d
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * @file metempl_device.c
+ *
+ * @brief template device class implementation.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <meids.h>
+#include "meerror.h"
+#include "mecommon.h"
+#include "meinternal.h"
+
+#include "medebug.h"
+#include "medevice.h"
+#include "metempl_device.h"
+#include "mesubdevice.h"
+#include "metempl_sub.h"
+
+me_device_t *metempl_pci_constructor(struct pci_dev *pci_device)
+{
+       metempl_device_t *metempl_device;
+       me_subdevice_t *subdevice;
+       unsigned int version_idx;
+       int err;
+       int i;
+
+       PDEBUG("executed.\n");
+
+       // Allocate structure for device instance.
+       metempl_device = kmalloc(sizeof(metempl_device_t), GFP_KERNEL);
+
+       if (!metempl_device) {
+               PERROR("Cannot get memory for device instance.\n");
+               return NULL;
+       }
+
+       memset(metempl_device, 0, sizeof(metempl_device_t));
+
+       // Initialize base class structure.
+       err = me_device_pci_init((me_device_t *) metempl_device, pci_device);
+
+       if (err) {
+               kfree(metempl_device);
+               PERROR("Cannot initialize device base class.\n");
+               return NULL;
+       }
+
+       /* Get the index in the device version information table. */
+       version_idx =
+           metempl_versions_get_device_index(metempl_device->base.info.pci.
+                                             device_id);
+
+       // Initialize spin lock .
+       spin_lock_init(&metempl_device->ctrl_reg_lock);
+
+       // Create subdevice instances.
+       for (i = 0; i < metempl_versions[version_idx].subdevices; i++) {
+               subdevice =
+                   (me_subdevice_t *) metempl_sub_constructor(metempl_device->
+                                                              base.info.pci.
+                                                              reg_bases[2], i,
+                                                              &metempl_device->
+                                                              ctrl_reg_lock);
+
+               if (!subdevice) {
+                       me_device_deinit((me_device_t *) metempl_device);
+                       kfree(metempl_device);
+                       PERROR("Cannot get memory for subdevice.\n");
+                       return NULL;
+               }
+
+               me_slist_add_subdevice_tail(&metempl_device->base.slist,
+                                           subdevice);
+       }
+
+       /* Overwrite base class methods if applicable. */
+
+       return (me_device_t *) metempl_device;
+}
+
+// Init and exit of module.
+
+static int __init metempl_init(void)
+{
+       PDEBUG("executed.\n.");
+       return 0;
+}
+
+static void __exit metempl_exit(void)
+{
+       PDEBUG("executed.\n.");
+}
+
+module_init(metempl_init);
+
+module_exit(metempl_exit);
+
+// Administrative stuff for modinfo.
+MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
+MODULE_DESCRIPTION("Device Driver Module for Template Device");
+MODULE_SUPPORTED_DEVICE("Meilhaus Template Devices");
+MODULE_LICENSE("GPL");
+
+// Export the constructor.
+EXPORT_SYMBOL(metempl_pci_constructor);
diff --git a/drivers/staging/meilhaus/metempl_device.h b/drivers/staging/meilhaus/metempl_device.h
new file mode 100644 (file)
index 0000000..3c3702c
--- /dev/null
@@ -0,0 +1,92 @@
+/**
+ * @file metempl_device.h
+ *
+ * @brief template device class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _METEMPL_DEVICE_H
+#define _METEMPL_DEVICE_H
+
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+#include "medevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief Structure holding template device capabilities.
+ */
+typedef struct metempl_version {
+       uint16_t device_id;
+       unsigned int subdevices;
+} metempl_version_t;
+
+/**
+ * @brief Device capabilities.
+ */
+static metempl_version_t metempl_versions[] = {
+       {0xDEAD, 1},
+       {0},
+};
+
+#define METEMPL_DEVICE_VERSIONS (sizeof(metempl_versions) / sizeof(metempl_version_t) - 1) /**< Returns the number of entries in #metempl_versions. */
+
+/**
+ * @brief Returns the index of the device entry in #metempl_versions.
+ *
+ * @param device_id The PCI device id of the device to query.
+ * @return The index of the device in #metempl_versions.
+ */
+static inline unsigned int metempl_versions_get_device_index(uint16_t device_id)
+{
+       unsigned int i;
+       for (i = 0; i < METEMPL_DEVICE_VERSIONS; i++)
+               if (metempl_versions[i].device_id == device_id)
+                       break;
+       return i;
+}
+
+/**
+ * @brief The template device class structure.
+ */
+typedef struct metempl_device {
+       me_device_t base;                       /**< The Meilhaus device base class. */
+
+       /* Child class attributes. */
+       spinlock_t ctrl_reg_lock;
+} metempl_device_t;
+
+/**
+ * @brief The template device class constructor.
+ *
+ * @param pci_device The pci device structure given by the PCI subsystem.
+ *
+ * @return On succes a new template device instance. \n
+ *         NULL on error.
+ */
+me_device_t *metempl_pci_constructor(struct pci_dev *pci_device)
+    __attribute__ ((weak));
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/metempl_sub.c b/drivers/staging/meilhaus/metempl_sub.c
new file mode 100644 (file)
index 0000000..f1d65d8
--- /dev/null
@@ -0,0 +1,149 @@
+/**
+ * @file metempl_sub.c
+ *
+ * @brief Subdevice instance.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 __KERNEL__
+#  define __KERNEL__
+#endif
+
+/*
+ * Includes
+ */
+#include <linux/module.h>
+
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <linux/types.h>
+
+#include "medefines.h"
+#include "meinternal.h"
+#include "meerror.h"
+
+#include "medebug.h"
+#include "metempl_sub_reg.h"
+#include "metempl_sub.h"
+
+/*
+ * Defines
+ */
+
+/*
+ * Functions
+ */
+
+static void metempl_sub_destructor(struct me_subdevice *subdevice)
+{
+       metempl_sub_subdevice_t *instance;
+
+       PDEBUG("executed.\n");
+       instance = (metempl_sub_subdevice_t *) subdevice;
+
+       /* Until there this was the things the default constructor does.
+          If you do not have any additional things to do you can wipe it out. */
+
+       me_subdevice_deinit(&instance->base);
+       kfree(instance);
+}
+
+static int metempl_sub_query_number_channels(me_subdevice_t * subdevice,
+                                            int *number)
+{
+       PDEBUG("executed.\n");
+       *number = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int metempl_sub_query_subdevice_type(me_subdevice_t * subdevice,
+                                           int *type, int *subtype)
+{
+       PDEBUG("executed.\n");
+       *type = 0;
+       *subtype = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+static int metempl_sub_query_subdevice_caps(me_subdevice_t * subdevice,
+                                           int *caps)
+{
+       PDEBUG("executed.\n");
+       *caps = 0;
+       return ME_ERRNO_SUCCESS;
+}
+
+metempl_sub_subdevice_t *metempl_sub_constructor(uint32_t reg_base,
+                                                unsigned int sub_idx,
+                                                spinlock_t * ctrl_reg_lock)
+{
+       metempl_sub_subdevice_t *subdevice;
+       int err;
+
+       PDEBUG("executed.\n");
+
+       /* Allocate memory for subdevice instance */
+       subdevice = kmalloc(sizeof(metempl_sub_subdevice_t), GFP_KERNEL);
+
+       if (!subdevice) {
+               PERROR("Cannot get memory for subdevice instance.\n");
+               return NULL;
+       }
+
+       memset(subdevice, 0, sizeof(metempl_sub_subdevice_t));
+
+       /* Check if subdevice index is out of range */
+
+       if (sub_idx >= 2) {
+               PERROR("Template subdevice index is out of range.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+
+       /* Initialize subdevice base class */
+       err = me_subdevice_init(&subdevice->base);
+
+       if (err) {
+               PERROR("Cannot initialize subdevice base class instance.\n");
+               kfree(subdevice);
+               return NULL;
+       }
+       // Initialize spin locks.
+       spin_lock_init(&subdevice->subdevice_lock);
+
+       subdevice->ctrl_reg_lock = ctrl_reg_lock;
+
+       /* Save the subdevice index */
+       subdevice->sub_idx = sub_idx;
+
+       /* Override base class methods. */
+       subdevice->base.me_subdevice_destructor = metempl_sub_destructor;
+       subdevice->base.me_subdevice_query_number_channels =
+           metempl_sub_query_number_channels;
+       subdevice->base.me_subdevice_query_subdevice_type =
+           metempl_sub_query_subdevice_type;
+       subdevice->base.me_subdevice_query_subdevice_caps =
+           metempl_sub_query_subdevice_caps;
+
+       return subdevice;
+}
diff --git a/drivers/staging/meilhaus/metempl_sub.h b/drivers/staging/meilhaus/metempl_sub.h
new file mode 100644 (file)
index 0000000..80c8af9
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * @file metempl_sub.h
+ *
+ * @brief Meilhaus subdevice class.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _METEMPL_SUB_H_
+#define _METEMPL_SUB_H_
+
+#include "mesubdevice.h"
+
+#ifdef __KERNEL__
+
+/**
+ * @brief The subdevice class.
+ */
+typedef struct metempl_sub_subdevice {
+       /* Inheritance */
+       me_subdevice_t base;                    /**< The subdevice base class. */
+
+       /* Attributes */
+       spinlock_t subdevice_lock;              /**< Spin lock to protect the subdevice from concurrent access. */
+       spinlock_t *ctrl_reg_lock;              /**< Spin lock to protect #ctrl_reg from concurrent access. */
+       int sub_idx;                            /**< The index of the subdevice on the device. */
+
+       unsigned long ctrl_reg;                 /**< Register to configure the modes. */
+} metempl_sub_subdevice_t;
+
+/**
+ * @brief The constructor to generate a subdevice instance.
+ *
+ * @param reg_base The register base address of the device as returned by the PCI BIOS.
+ * @param sub_idx The index of the subdevice on the device.
+ * @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
+ *
+ * @return Pointer to new instance on success.\n
+ * NULL on error.
+ */
+metempl_sub_subdevice_t *metempl_sub_constructor(uint32_t reg_base,
+                                                unsigned int sub_idx,
+                                                spinlock_t * ctrl_reg_lock);
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/metempl_sub_reg.h b/drivers/staging/meilhaus/metempl_sub_reg.h
new file mode 100644 (file)
index 0000000..1a2cab7
--- /dev/null
@@ -0,0 +1,35 @@
+/**
+ * @file metempl_sub_reg.h
+ *
+ * @brief Subdevice register definitions.
+ * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ * @author Guenter Gebhardt
+ */
+
+/*
+ * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * This file 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 _METEMPL_SUB_REG_H_
+#define _METEMPL_SUB_REG_H_
+
+#ifdef __KERNEL__
+
+#define METEMPL_PORT_MODE                      0x0010          /**< Configuration register. */
+
+#endif
+#endif
diff --git a/drivers/staging/meilhaus/metypes.h b/drivers/staging/meilhaus/metypes.h
new file mode 100644 (file)
index 0000000..228ea15
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2005 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : metypes.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _METYPES_H_
+#define _METYPES_H_
+
+
+typedef int (*meErrorCB_t)(char *pcFunctionName, int iErrorCode);
+
+typedef int (*meIOStreamCB_t)(
+               int iDevice,
+               int iSubdevice,
+               int iCount,
+               void *pvContext,
+               int iErrorCode);
+
+typedef int (*meIOIrqCB_t)(
+               int iDevice,
+               int iSubdevice,
+               int iChannel,
+               int iIrqCount,
+               int iValue,
+               void *pvContext,
+               int iErrorCode);
+
+
+typedef struct meIOSingle {
+       int iDevice;
+       int iSubdevice;
+       int iChannel;
+       int iDir;
+       int iValue;
+       int iTimeOut;
+       int iFlags;
+       int iErrno;
+} meIOSingle_t;
+
+
+typedef struct meIOStreamConfig {
+       int iChannel;
+       int iStreamConfig;
+       int iRef;
+       int iFlags;
+} meIOStreamConfig_t;
+
+
+typedef struct meIOStreamTrigger {
+       int iAcqStartTrigType;
+       int iAcqStartTrigEdge;
+       int iAcqStartTrigChan;
+       int iAcqStartTicksLow;
+       int iAcqStartTicksHigh;
+       int iAcqStartArgs[10];
+       int iScanStartTrigType;
+       int iScanStartTicksLow;
+       int iScanStartTicksHigh;
+       int iScanStartArgs[10];
+       int iConvStartTrigType;
+       int iConvStartTicksLow;
+       int iConvStartTicksHigh;
+       int iConvStartArgs[10];
+       int iScanStopTrigType;
+       int iScanStopCount;
+       int iScanStopArgs[10];
+       int iAcqStopTrigType;
+       int iAcqStopCount;
+       int iAcqStopArgs[10];
+       int iFlags;
+} meIOStreamTrigger_t;
+
+
+typedef struct meIOStreamStart {
+       int iDevice;
+       int iSubdevice;
+       int iStartMode;
+       int iTimeOut;
+       int iFlags;
+       int iErrno;
+} meIOStreamStart_t;
+
+
+typedef struct meIOStreamStop {
+       int iDevice;
+       int iSubdevice;
+       int iStopMode;
+       int iFlags;
+       int iErrno;
+} meIOStreamStop_t;
+
+
+#endif