diff options
author | David Kiliani <mail@davidkiliani.de> | 2008-11-01 00:39:12 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-06 13:52:29 -0800 |
commit | 3fedd14818592016f7ffd84dfe134881b3896ecf (patch) | |
tree | 22574873dc7d844d4cc33d04f7e6ba03f23c639e /drivers | |
parent | 5ec5ec78060481e6a0cecc06ab0c6ec8b213ec80 (diff) | |
download | op-kernel-dev-3fedd14818592016f7ffd84dfe134881b3896ecf.zip op-kernel-dev-3fedd14818592016f7ffd84dfe134881b3896ecf.tar.gz |
Staging: Add the Meilhaus ME-IDS driver package
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>
Diffstat (limited to 'drivers')
133 files changed, 41371 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index d8f26ac..a3e361db 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -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" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index dd9a625..06613bb 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -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 index 0000000..6def83f --- /dev/null +++ b/drivers/staging/meilhaus/Kconfig @@ -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 index 0000000..5ab2c1c --- /dev/null +++ b/drivers/staging/meilhaus/Makefile @@ -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 index 0000000..6ec2520 --- /dev/null +++ b/drivers/staging/meilhaus/TODO @@ -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 index 0000000..8950e47 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_device.c @@ -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 index 0000000..d93a8ae --- /dev/null +++ b/drivers/staging/meilhaus/me0600_device.h @@ -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 index 0000000..3a27757 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_dio.c @@ -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 index 0000000..5d075c7 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_dio.h @@ -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 index 0000000..f116ea3 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_dio_reg.h @@ -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 index 0000000..a449ab2 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_ext_irq.c @@ -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 index 0000000..f5f2204 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_ext_irq.h @@ -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 index 0000000..f6198fa --- /dev/null +++ b/drivers/staging/meilhaus/me0600_ext_irq_reg.h @@ -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 index 0000000..b6d977f --- /dev/null +++ b/drivers/staging/meilhaus/me0600_optoi.c @@ -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 index 0000000..e7e69bc --- /dev/null +++ b/drivers/staging/meilhaus/me0600_optoi.h @@ -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 index 0000000..e0bc450 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_optoi_reg.h @@ -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 index 0000000..2665c69 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_relay.c @@ -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 index 0000000..2ce7dca --- /dev/null +++ b/drivers/staging/meilhaus/me0600_relay.h @@ -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 index 0000000..ba4db2e --- /dev/null +++ b/drivers/staging/meilhaus/me0600_relay_reg.h @@ -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 index 0000000..ab8e13b --- /dev/null +++ b/drivers/staging/meilhaus/me0600_ttli.c @@ -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 index 0000000..6c90396 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_ttli.h @@ -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 index 0000000..4f986d16 --- /dev/null +++ b/drivers/staging/meilhaus/me0600_ttli_reg.h @@ -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 index 0000000..764d5d3 --- /dev/null +++ b/drivers/staging/meilhaus/me0900_device.c @@ -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 index 0000000..bd17f25 --- /dev/null +++ b/drivers/staging/meilhaus/me0900_device.h @@ -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 index 0000000..d7d7394 --- /dev/null +++ b/drivers/staging/meilhaus/me0900_di.c @@ -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 index 0000000..014f134 --- /dev/null +++ b/drivers/staging/meilhaus/me0900_di.h @@ -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 index 0000000..b5b9c3a --- /dev/null +++ b/drivers/staging/meilhaus/me0900_do.c @@ -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 index 0000000..13e8a8b --- /dev/null +++ b/drivers/staging/meilhaus/me0900_do.h @@ -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 index 0000000..3bf163b --- /dev/null +++ b/drivers/staging/meilhaus/me0900_reg.h @@ -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 index 0000000..c44e214 --- /dev/null +++ b/drivers/staging/meilhaus/me1000_device.c @@ -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 index 0000000..cbbe126 --- /dev/null +++ b/drivers/staging/meilhaus/me1000_device.h @@ -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 index 0000000..87605a9 --- /dev/null +++ b/drivers/staging/meilhaus/me1000_dio.c @@ -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 index 0000000..d26e93f --- /dev/null +++ b/drivers/staging/meilhaus/me1000_dio.h @@ -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 index 0000000..4d5b38d --- /dev/null +++ b/drivers/staging/meilhaus/me1000_dio_reg.h @@ -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 index 0000000..b95bb4f --- /dev/null +++ b/drivers/staging/meilhaus/me1400_device.c @@ -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 index 0000000..6215b25 --- /dev/null +++ b/drivers/staging/meilhaus/me1400_device.h @@ -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 index 0000000..b8c2696 --- /dev/null +++ b/drivers/staging/meilhaus/me1400_ext_irq.c @@ -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 index 0000000..9b72a04 --- /dev/null +++ b/drivers/staging/meilhaus/me1400_ext_irq.h @@ -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 index 0000000..c9740f2 --- /dev/null +++ b/drivers/staging/meilhaus/me1400_ext_irq_reg.h @@ -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 index 0000000..6f26665 --- /dev/null +++ b/drivers/staging/meilhaus/me1600_ao.c @@ -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 index 0000000..b82bf5a --- /dev/null +++ b/drivers/staging/meilhaus/me1600_ao.h @@ -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 index 0000000..31e7800 --- /dev/null +++ b/drivers/staging/meilhaus/me1600_ao_reg.h @@ -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 index 0000000..3bc2cb1 --- /dev/null +++ b/drivers/staging/meilhaus/me1600_device.c @@ -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 index 0000000..f7b231f --- /dev/null +++ b/drivers/staging/meilhaus/me1600_device.h @@ -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 index 0000000..1a0de5d --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ai.c @@ -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 index 0000000..1d5a1b9 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ai.h @@ -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 index 0000000..083fac7 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ai_reg.h @@ -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 index 0000000..2c92e65 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ao.c @@ -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 index 0000000..6fbc4a2 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ao.h @@ -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 index 0000000..f83d82e --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ao_reg.h @@ -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 index 0000000..fa45584 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_device.c @@ -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 index 0000000..fa812d4 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_device.h @@ -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 index 0000000..7e3c9f4 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_di.c @@ -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 index 0000000..ec8b175 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_di.h @@ -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 index 0000000..0af95d1 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_dio.c @@ -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 index 0000000..4625ba9 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_dio.h @@ -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 index 0000000..7a4016a --- /dev/null +++ b/drivers/staging/meilhaus/me4600_dio_reg.h @@ -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 index 0000000..ee591bc --- /dev/null +++ b/drivers/staging/meilhaus/me4600_do.c @@ -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 index 0000000..e838564 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_do.h @@ -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 index 0000000..8a10dce --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ext_irq.c @@ -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 index 0000000..3c7b27f --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ext_irq.h @@ -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 index 0000000..898e1e7 --- /dev/null +++ b/drivers/staging/meilhaus/me4600_ext_irq_reg.h @@ -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 index 0000000..ae152bbc --- /dev/null +++ b/drivers/staging/meilhaus/me4600_reg.h @@ -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 index 0000000..3f5ff6d --- /dev/null +++ b/drivers/staging/meilhaus/me6000_ao.c @@ -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 index 0000000..9629649 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_ao.h @@ -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 index 0000000..eb8f46e --- /dev/null +++ b/drivers/staging/meilhaus/me6000_ao_reg.h @@ -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 index 0000000..fee4c58 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_device.c @@ -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 index 0000000..18cc7d1 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_device.h @@ -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 index 0000000..07f1069 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_dio.c @@ -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 index 0000000..858bec1 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_dio.h @@ -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 index 0000000..e67a791 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_dio_reg.h @@ -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 index 0000000..d352730 --- /dev/null +++ b/drivers/staging/meilhaus/me6000_reg.h @@ -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 index 0000000..1fb79e4 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_device.c @@ -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 index 0000000..44c42ef --- /dev/null +++ b/drivers/staging/meilhaus/me8100_device.h @@ -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 index 0000000..0f14637 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_di.c @@ -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 index 0000000..e1db791 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_di.h @@ -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 index 0000000..063bd19 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_di_reg.h @@ -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 index 0000000..957b9f9 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_do.c @@ -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 index 0000000..acf8801 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_do.h @@ -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 index 0000000..13a2380 --- /dev/null +++ b/drivers/staging/meilhaus/me8100_do_reg.h @@ -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 index 0000000..d8c4b1c --- /dev/null +++ b/drivers/staging/meilhaus/me8100_reg.h @@ -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 index 0000000..261c0cb --- /dev/null +++ b/drivers/staging/meilhaus/me8200_device.c @@ -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 index 0000000..cbd2a01 --- /dev/null +++ b/drivers/staging/meilhaus/me8200_device.h @@ -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 index 0000000..0bb4567 --- /dev/null +++ b/drivers/staging/meilhaus/me8200_di.c @@ -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 index 0000000..2a3b005 --- /dev/null +++ b/drivers/staging/meilhaus/me8200_di.h @@ -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 index 0000000..b9a619d --- /dev/null +++ b/drivers/staging/meilhaus/me8200_di_reg.h @@ -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 index 0000000..ff8ca1b --- /dev/null +++ b/drivers/staging/meilhaus/me8200_dio.c @@ -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 index 0000000..9ddd93d --- /dev/null +++ b/drivers/staging/meilhaus/me8200_dio.h @@ -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 index 0000000..ac94a133 --- /dev/null +++ b/drivers/staging/meilhaus/me8200_dio_reg.h @@ -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 index 0000000..5f4ba5d --- /dev/null +++ b/drivers/staging/meilhaus/me8200_do.c @@ -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 index 0000000..2758125 --- /dev/null +++ b/drivers/staging/meilhaus/me8200_do.h @@ -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 index 0000000..4109504 --- /dev/null +++ b/drivers/staging/meilhaus/me8200_do_reg.h @@ -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 index 0000000..a73fe4d --- /dev/null +++ b/drivers/staging/meilhaus/me8200_reg.h @@ -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 index 0000000..6e44c3d --- /dev/null +++ b/drivers/staging/meilhaus/me8254.c @@ -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 index 0000000..572b719 --- /dev/null +++ b/drivers/staging/meilhaus/me8254.h @@ -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 index 0000000..7e2c36b --- /dev/null +++ b/drivers/staging/meilhaus/me8254_reg.h @@ -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 index 0000000..180e7f8 --- /dev/null +++ b/drivers/staging/meilhaus/me8255.c @@ -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 index 0000000..3382300 --- /dev/null +++ b/drivers/staging/meilhaus/me8255.h @@ -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 index 0000000..d1dea1a --- /dev/null +++ b/drivers/staging/meilhaus/me8255_reg.h @@ -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 index 0000000..e9b591e --- /dev/null +++ b/drivers/staging/meilhaus/mecirc_buf.h @@ -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 index 0000000..ef47c38 --- /dev/null +++ b/drivers/staging/meilhaus/mecommon.h @@ -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 index 0000000..382d00f --- /dev/null +++ b/drivers/staging/meilhaus/medebug.h @@ -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 index 0000000..6158ef5 --- /dev/null +++ b/drivers/staging/meilhaus/medefines.h @@ -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 index 0000000..8f62e16 --- /dev/null +++ b/drivers/staging/meilhaus/medevice.c @@ -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 index 0000000..25da828 --- /dev/null +++ b/drivers/staging/meilhaus/medevice.h @@ -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 index 0000000..ef4e369 --- /dev/null +++ b/drivers/staging/meilhaus/medlist.c @@ -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 index 0000000..091c11e --- /dev/null +++ b/drivers/staging/meilhaus/medlist.h @@ -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 index 0000000..f649e3d --- /dev/null +++ b/drivers/staging/meilhaus/medlock.c @@ -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 index 0000000..4d6ddc8 --- /dev/null +++ b/drivers/staging/meilhaus/medlock.h @@ -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 index 0000000..02e2408 --- /dev/null +++ b/drivers/staging/meilhaus/medriver.h @@ -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 index 0000000..6a9f08d --- /dev/null +++ b/drivers/staging/meilhaus/medummy.c @@ -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 index 0000000..717000f --- /dev/null +++ b/drivers/staging/meilhaus/medummy.h @@ -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 index 0000000..9eda4bf --- /dev/null +++ b/drivers/staging/meilhaus/meerror.h @@ -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 index 0000000..c07d202 --- /dev/null +++ b/drivers/staging/meilhaus/mefirmware.c @@ -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 index 0000000..a268508 --- /dev/null +++ b/drivers/staging/meilhaus/mefirmware.h @@ -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 index 0000000..b3e757c --- /dev/null +++ b/drivers/staging/meilhaus/meids.h @@ -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 index 0000000..8d126b4 --- /dev/null +++ b/drivers/staging/meilhaus/meinternal.h @@ -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 index 0000000..6dc719f --- /dev/null +++ b/drivers/staging/meilhaus/meioctl.h @@ -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 index 0000000..6cdeb85 --- /dev/null +++ b/drivers/staging/meilhaus/memain.c @@ -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 index 0000000..7616ff7 --- /dev/null +++ b/drivers/staging/meilhaus/memain.h @@ -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 index 0000000..1868614 --- /dev/null +++ b/drivers/staging/meilhaus/meplx_reg.h @@ -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 index 0000000..7e8b66c --- /dev/null +++ b/drivers/staging/meilhaus/meslist.c @@ -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 index 0000000..d26c896 --- /dev/null +++ b/drivers/staging/meilhaus/meslist.h @@ -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 index 0000000..5230b89 --- /dev/null +++ b/drivers/staging/meilhaus/meslock.c @@ -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 index 0000000..f42b25c --- /dev/null +++ b/drivers/staging/meilhaus/meslock.h @@ -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 index 0000000..98d4f1f --- /dev/null +++ b/drivers/staging/meilhaus/mesubdevice.c @@ -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 index 0000000..19ec2b5 --- /dev/null +++ b/drivers/staging/meilhaus/mesubdevice.h @@ -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 index 0000000..e48632d --- /dev/null +++ b/drivers/staging/meilhaus/metempl_device.c @@ -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 index 0000000..3c3702c --- /dev/null +++ b/drivers/staging/meilhaus/metempl_device.h @@ -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 index 0000000..f1d65d8 --- /dev/null +++ b/drivers/staging/meilhaus/metempl_sub.c @@ -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 index 0000000..80c8af9 --- /dev/null +++ b/drivers/staging/meilhaus/metempl_sub.h @@ -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 index 0000000..1a2cab7 --- /dev/null +++ b/drivers/staging/meilhaus/metempl_sub_reg.h @@ -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 index 0000000..228ea15 --- /dev/null +++ b/drivers/staging/meilhaus/metypes.h @@ -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 |