From 3fedd14818592016f7ffd84dfe134881b3896ecf Mon Sep 17 00:00:00 2001 From: David Kiliani Date: Sat, 1 Nov 2008 00:39:12 +0100 Subject: Staging: Add the Meilhaus ME-IDS driver package Originally written by Guenter Gebhardt and Krzysztof Gantzke 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 Cc: Guenter Gebhardt Cc: Krzysztof Gantzke Signed-off-by: Greg Kroah-Hartman --- drivers/staging/meilhaus/Kconfig | 127 + drivers/staging/meilhaus/Makefile | 43 + drivers/staging/meilhaus/TODO | 10 + drivers/staging/meilhaus/me0600_device.c | 215 + drivers/staging/meilhaus/me0600_device.h | 97 + drivers/staging/meilhaus/me0600_dio.c | 415 ++ drivers/staging/meilhaus/me0600_dio.h | 68 + drivers/staging/meilhaus/me0600_dio_reg.h | 41 + drivers/staging/meilhaus/me0600_ext_irq.c | 478 ++ drivers/staging/meilhaus/me0600_ext_irq.h | 58 + drivers/staging/meilhaus/me0600_ext_irq_reg.h | 18 + drivers/staging/meilhaus/me0600_optoi.c | 243 + drivers/staging/meilhaus/me0600_optoi.h | 58 + drivers/staging/meilhaus/me0600_optoi_reg.h | 35 + drivers/staging/meilhaus/me0600_relay.c | 359 ++ drivers/staging/meilhaus/me0600_relay.h | 63 + drivers/staging/meilhaus/me0600_relay_reg.h | 36 + drivers/staging/meilhaus/me0600_ttli.c | 238 + drivers/staging/meilhaus/me0600_ttli.h | 58 + drivers/staging/meilhaus/me0600_ttli_reg.h | 35 + drivers/staging/meilhaus/me0900_device.c | 180 + drivers/staging/meilhaus/me0900_device.h | 92 + drivers/staging/meilhaus/me0900_di.c | 246 + drivers/staging/meilhaus/me0900_di.h | 65 + drivers/staging/meilhaus/me0900_do.c | 314 ++ drivers/staging/meilhaus/me0900_do.h | 68 + drivers/staging/meilhaus/me0900_reg.h | 40 + drivers/staging/meilhaus/me1000_device.c | 208 + drivers/staging/meilhaus/me1000_device.h | 59 + drivers/staging/meilhaus/me1000_dio.c | 438 ++ drivers/staging/meilhaus/me1000_dio.h | 71 + drivers/staging/meilhaus/me1000_dio_reg.h | 50 + drivers/staging/meilhaus/me1400_device.c | 256 ++ drivers/staging/meilhaus/me1400_device.h | 108 + drivers/staging/meilhaus/me1400_ext_irq.c | 517 +++ drivers/staging/meilhaus/me1400_ext_irq.h | 62 + drivers/staging/meilhaus/me1400_ext_irq_reg.h | 56 + drivers/staging/meilhaus/me1600_ao.c | 1033 +++++ drivers/staging/meilhaus/me1600_ao.h | 132 + drivers/staging/meilhaus/me1600_ao_reg.h | 66 + drivers/staging/meilhaus/me1600_device.c | 261 ++ drivers/staging/meilhaus/me1600_device.h | 101 + drivers/staging/meilhaus/me4600_ai.c | 3434 ++++++++++++++ drivers/staging/meilhaus/me4600_ai.h | 180 + drivers/staging/meilhaus/me4600_ai_reg.h | 107 + drivers/staging/meilhaus/me4600_ao.c | 6011 +++++++++++++++++++++++++ drivers/staging/meilhaus/me4600_ao.h | 263 ++ drivers/staging/meilhaus/me4600_ao_reg.h | 113 + drivers/staging/meilhaus/me4600_device.c | 373 ++ drivers/staging/meilhaus/me4600_device.h | 151 + drivers/staging/meilhaus/me4600_di.c | 256 ++ drivers/staging/meilhaus/me4600_di.h | 64 + drivers/staging/meilhaus/me4600_dio.c | 510 +++ drivers/staging/meilhaus/me4600_dio.h | 69 + drivers/staging/meilhaus/me4600_dio_reg.h | 63 + drivers/staging/meilhaus/me4600_do.c | 433 ++ drivers/staging/meilhaus/me4600_do.h | 65 + drivers/staging/meilhaus/me4600_ext_irq.c | 467 ++ drivers/staging/meilhaus/me4600_ext_irq.h | 78 + drivers/staging/meilhaus/me4600_ext_irq_reg.h | 41 + drivers/staging/meilhaus/me4600_reg.h | 46 + drivers/staging/meilhaus/me6000_ao.c | 3739 +++++++++++++++ drivers/staging/meilhaus/me6000_ao.h | 200 + drivers/staging/meilhaus/me6000_ao_reg.h | 177 + drivers/staging/meilhaus/me6000_device.c | 211 + drivers/staging/meilhaus/me6000_device.h | 149 + drivers/staging/meilhaus/me6000_dio.c | 415 ++ drivers/staging/meilhaus/me6000_dio.h | 68 + drivers/staging/meilhaus/me6000_dio_reg.h | 43 + drivers/staging/meilhaus/me6000_reg.h | 35 + drivers/staging/meilhaus/me8100_device.c | 187 + drivers/staging/meilhaus/me8100_device.h | 97 + drivers/staging/meilhaus/me8100_di.c | 693 +++ drivers/staging/meilhaus/me8100_di.h | 89 + drivers/staging/meilhaus/me8100_di_reg.h | 47 + drivers/staging/meilhaus/me8100_do.c | 391 ++ drivers/staging/meilhaus/me8100_do.h | 70 + drivers/staging/meilhaus/me8100_do_reg.h | 36 + drivers/staging/meilhaus/me8100_reg.h | 41 + drivers/staging/meilhaus/me8200_device.c | 194 + drivers/staging/meilhaus/me8200_device.h | 97 + drivers/staging/meilhaus/me8200_di.c | 857 ++++ drivers/staging/meilhaus/me8200_di.h | 92 + drivers/staging/meilhaus/me8200_di_reg.h | 75 + drivers/staging/meilhaus/me8200_dio.c | 418 ++ drivers/staging/meilhaus/me8200_dio.h | 68 + drivers/staging/meilhaus/me8200_dio_reg.h | 43 + drivers/staging/meilhaus/me8200_do.c | 600 +++ drivers/staging/meilhaus/me8200_do.h | 75 + drivers/staging/meilhaus/me8200_do_reg.h | 40 + drivers/staging/meilhaus/me8200_reg.h | 46 + drivers/staging/meilhaus/me8254.c | 1176 +++++ drivers/staging/meilhaus/me8254.h | 80 + drivers/staging/meilhaus/me8254_reg.h | 172 + drivers/staging/meilhaus/me8255.c | 462 ++ drivers/staging/meilhaus/me8255.h | 59 + drivers/staging/meilhaus/me8255_reg.h | 50 + drivers/staging/meilhaus/mecirc_buf.h | 131 + drivers/staging/meilhaus/mecommon.h | 26 + drivers/staging/meilhaus/medebug.h | 125 + drivers/staging/meilhaus/medefines.h | 449 ++ drivers/staging/meilhaus/medevice.c | 1740 +++++++ drivers/staging/meilhaus/medevice.h | 304 ++ drivers/staging/meilhaus/medlist.c | 127 + drivers/staging/meilhaus/medlist.h | 91 + drivers/staging/meilhaus/medlock.c | 195 + drivers/staging/meilhaus/medlock.h | 76 + drivers/staging/meilhaus/medriver.h | 350 ++ drivers/staging/meilhaus/medummy.c | 1266 ++++++ drivers/staging/meilhaus/medummy.h | 40 + drivers/staging/meilhaus/meerror.h | 100 + drivers/staging/meilhaus/mefirmware.c | 137 + drivers/staging/meilhaus/mefirmware.h | 57 + drivers/staging/meilhaus/meids.h | 31 + drivers/staging/meilhaus/meinternal.h | 363 ++ drivers/staging/meilhaus/meioctl.h | 515 +++ drivers/staging/meilhaus/memain.c | 2022 +++++++++ drivers/staging/meilhaus/memain.h | 460 ++ drivers/staging/meilhaus/meplx_reg.h | 53 + drivers/staging/meilhaus/meslist.c | 173 + drivers/staging/meilhaus/meslist.h | 108 + drivers/staging/meilhaus/meslock.c | 136 + drivers/staging/meilhaus/meslock.h | 73 + drivers/staging/meilhaus/mesubdevice.c | 317 ++ drivers/staging/meilhaus/mesubdevice.h | 197 + drivers/staging/meilhaus/metempl_device.c | 137 + drivers/staging/meilhaus/metempl_device.h | 92 + drivers/staging/meilhaus/metempl_sub.c | 149 + drivers/staging/meilhaus/metempl_sub.h | 64 + drivers/staging/meilhaus/metempl_sub_reg.h | 35 + drivers/staging/meilhaus/metypes.h | 95 + 131 files changed, 41368 insertions(+) create mode 100644 drivers/staging/meilhaus/Kconfig create mode 100644 drivers/staging/meilhaus/Makefile create mode 100644 drivers/staging/meilhaus/TODO create mode 100644 drivers/staging/meilhaus/me0600_device.c create mode 100644 drivers/staging/meilhaus/me0600_device.h create mode 100644 drivers/staging/meilhaus/me0600_dio.c create mode 100644 drivers/staging/meilhaus/me0600_dio.h create mode 100644 drivers/staging/meilhaus/me0600_dio_reg.h create mode 100644 drivers/staging/meilhaus/me0600_ext_irq.c create mode 100644 drivers/staging/meilhaus/me0600_ext_irq.h create mode 100644 drivers/staging/meilhaus/me0600_ext_irq_reg.h create mode 100644 drivers/staging/meilhaus/me0600_optoi.c create mode 100644 drivers/staging/meilhaus/me0600_optoi.h create mode 100644 drivers/staging/meilhaus/me0600_optoi_reg.h create mode 100644 drivers/staging/meilhaus/me0600_relay.c create mode 100644 drivers/staging/meilhaus/me0600_relay.h create mode 100644 drivers/staging/meilhaus/me0600_relay_reg.h create mode 100644 drivers/staging/meilhaus/me0600_ttli.c create mode 100644 drivers/staging/meilhaus/me0600_ttli.h create mode 100644 drivers/staging/meilhaus/me0600_ttli_reg.h create mode 100644 drivers/staging/meilhaus/me0900_device.c create mode 100644 drivers/staging/meilhaus/me0900_device.h create mode 100644 drivers/staging/meilhaus/me0900_di.c create mode 100644 drivers/staging/meilhaus/me0900_di.h create mode 100644 drivers/staging/meilhaus/me0900_do.c create mode 100644 drivers/staging/meilhaus/me0900_do.h create mode 100644 drivers/staging/meilhaus/me0900_reg.h create mode 100644 drivers/staging/meilhaus/me1000_device.c create mode 100644 drivers/staging/meilhaus/me1000_device.h create mode 100644 drivers/staging/meilhaus/me1000_dio.c create mode 100644 drivers/staging/meilhaus/me1000_dio.h create mode 100644 drivers/staging/meilhaus/me1000_dio_reg.h create mode 100644 drivers/staging/meilhaus/me1400_device.c create mode 100644 drivers/staging/meilhaus/me1400_device.h create mode 100644 drivers/staging/meilhaus/me1400_ext_irq.c create mode 100644 drivers/staging/meilhaus/me1400_ext_irq.h create mode 100644 drivers/staging/meilhaus/me1400_ext_irq_reg.h create mode 100644 drivers/staging/meilhaus/me1600_ao.c create mode 100644 drivers/staging/meilhaus/me1600_ao.h create mode 100644 drivers/staging/meilhaus/me1600_ao_reg.h create mode 100644 drivers/staging/meilhaus/me1600_device.c create mode 100644 drivers/staging/meilhaus/me1600_device.h create mode 100644 drivers/staging/meilhaus/me4600_ai.c create mode 100644 drivers/staging/meilhaus/me4600_ai.h create mode 100644 drivers/staging/meilhaus/me4600_ai_reg.h create mode 100644 drivers/staging/meilhaus/me4600_ao.c create mode 100644 drivers/staging/meilhaus/me4600_ao.h create mode 100644 drivers/staging/meilhaus/me4600_ao_reg.h create mode 100644 drivers/staging/meilhaus/me4600_device.c create mode 100644 drivers/staging/meilhaus/me4600_device.h create mode 100644 drivers/staging/meilhaus/me4600_di.c create mode 100644 drivers/staging/meilhaus/me4600_di.h create mode 100644 drivers/staging/meilhaus/me4600_dio.c create mode 100644 drivers/staging/meilhaus/me4600_dio.h create mode 100644 drivers/staging/meilhaus/me4600_dio_reg.h create mode 100644 drivers/staging/meilhaus/me4600_do.c create mode 100644 drivers/staging/meilhaus/me4600_do.h create mode 100644 drivers/staging/meilhaus/me4600_ext_irq.c create mode 100644 drivers/staging/meilhaus/me4600_ext_irq.h create mode 100644 drivers/staging/meilhaus/me4600_ext_irq_reg.h create mode 100644 drivers/staging/meilhaus/me4600_reg.h create mode 100644 drivers/staging/meilhaus/me6000_ao.c create mode 100644 drivers/staging/meilhaus/me6000_ao.h create mode 100644 drivers/staging/meilhaus/me6000_ao_reg.h create mode 100644 drivers/staging/meilhaus/me6000_device.c create mode 100644 drivers/staging/meilhaus/me6000_device.h create mode 100644 drivers/staging/meilhaus/me6000_dio.c create mode 100644 drivers/staging/meilhaus/me6000_dio.h create mode 100644 drivers/staging/meilhaus/me6000_dio_reg.h create mode 100644 drivers/staging/meilhaus/me6000_reg.h create mode 100644 drivers/staging/meilhaus/me8100_device.c create mode 100644 drivers/staging/meilhaus/me8100_device.h create mode 100644 drivers/staging/meilhaus/me8100_di.c create mode 100644 drivers/staging/meilhaus/me8100_di.h create mode 100644 drivers/staging/meilhaus/me8100_di_reg.h create mode 100644 drivers/staging/meilhaus/me8100_do.c create mode 100644 drivers/staging/meilhaus/me8100_do.h create mode 100644 drivers/staging/meilhaus/me8100_do_reg.h create mode 100644 drivers/staging/meilhaus/me8100_reg.h create mode 100644 drivers/staging/meilhaus/me8200_device.c create mode 100644 drivers/staging/meilhaus/me8200_device.h create mode 100644 drivers/staging/meilhaus/me8200_di.c create mode 100644 drivers/staging/meilhaus/me8200_di.h create mode 100644 drivers/staging/meilhaus/me8200_di_reg.h create mode 100644 drivers/staging/meilhaus/me8200_dio.c create mode 100644 drivers/staging/meilhaus/me8200_dio.h create mode 100644 drivers/staging/meilhaus/me8200_dio_reg.h create mode 100644 drivers/staging/meilhaus/me8200_do.c create mode 100644 drivers/staging/meilhaus/me8200_do.h create mode 100644 drivers/staging/meilhaus/me8200_do_reg.h create mode 100644 drivers/staging/meilhaus/me8200_reg.h create mode 100644 drivers/staging/meilhaus/me8254.c create mode 100644 drivers/staging/meilhaus/me8254.h create mode 100644 drivers/staging/meilhaus/me8254_reg.h create mode 100644 drivers/staging/meilhaus/me8255.c create mode 100644 drivers/staging/meilhaus/me8255.h create mode 100644 drivers/staging/meilhaus/me8255_reg.h create mode 100644 drivers/staging/meilhaus/mecirc_buf.h create mode 100644 drivers/staging/meilhaus/mecommon.h create mode 100644 drivers/staging/meilhaus/medebug.h create mode 100644 drivers/staging/meilhaus/medefines.h create mode 100644 drivers/staging/meilhaus/medevice.c create mode 100644 drivers/staging/meilhaus/medevice.h create mode 100644 drivers/staging/meilhaus/medlist.c create mode 100644 drivers/staging/meilhaus/medlist.h create mode 100644 drivers/staging/meilhaus/medlock.c create mode 100644 drivers/staging/meilhaus/medlock.h create mode 100644 drivers/staging/meilhaus/medriver.h create mode 100644 drivers/staging/meilhaus/medummy.c create mode 100644 drivers/staging/meilhaus/medummy.h create mode 100644 drivers/staging/meilhaus/meerror.h create mode 100644 drivers/staging/meilhaus/mefirmware.c create mode 100644 drivers/staging/meilhaus/mefirmware.h create mode 100644 drivers/staging/meilhaus/meids.h create mode 100644 drivers/staging/meilhaus/meinternal.h create mode 100644 drivers/staging/meilhaus/meioctl.h create mode 100644 drivers/staging/meilhaus/memain.c create mode 100644 drivers/staging/meilhaus/memain.h create mode 100644 drivers/staging/meilhaus/meplx_reg.h create mode 100644 drivers/staging/meilhaus/meslist.c create mode 100644 drivers/staging/meilhaus/meslist.h create mode 100644 drivers/staging/meilhaus/meslock.c create mode 100644 drivers/staging/meilhaus/meslock.h create mode 100644 drivers/staging/meilhaus/mesubdevice.c create mode 100644 drivers/staging/meilhaus/mesubdevice.h create mode 100644 drivers/staging/meilhaus/metempl_device.c create mode 100644 drivers/staging/meilhaus/metempl_device.h create mode 100644 drivers/staging/meilhaus/metempl_sub.c create mode 100644 drivers/staging/meilhaus/metempl_sub.h create mode 100644 drivers/staging/meilhaus/metempl_sub_reg.h create mode 100644 drivers/staging/meilhaus/metypes.h (limited to 'drivers/staging/meilhaus') diff --git a/drivers/staging/meilhaus/Kconfig b/drivers/staging/meilhaus/Kconfig new file mode 100644 index 000000000000..6def83fa2c96 --- /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 000000000000..5ab2c1c9c861 --- /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 000000000000..6ec25203089c --- /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 +and CC: David Kiliani diff --git a/drivers/staging/meilhaus/me0600_device.c b/drivers/staging/meilhaus/me0600_device.c new file mode 100644 index 000000000000..8950e47e0e86 --- /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 + +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..d93a8aee581b --- /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 +#include + +#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 000000000000..3a2775749a2c --- /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 + +#include +#include +#include +#include + +#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 000000000000..5d075c7d6882 --- /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 000000000000..f116ea3b79d2 --- /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 000000000000..a449ab200940 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 000000000000..f5f2204b49a0 --- /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 + +#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 000000000000..f6198fa6d2b2 --- /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 000000000000..b6d977f228ca --- /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 + +#include +#include +#include +#include + +#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 000000000000..e7e69bcde9c9 --- /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 000000000000..e0bc45054000 --- /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 000000000000..2665c69addd2 --- /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 + +#include +#include +#include +#include + +#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 000000000000..2ce7dcab8b39 --- /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 000000000000..ba4db2e223c5 --- /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 000000000000..ab8e13b6f329 --- /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 + +#include +#include +#include +#include + +#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 000000000000..6c9039614867 --- /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 000000000000..4f986d160934 --- /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 000000000000..764d5d307c44 --- /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 + +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..bd17f2521511 --- /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 +#include + +#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 000000000000..d7d7394f800a --- /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 + +#include +#include +#include +#include +#include +#include + +#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 000000000000..014f1348fc9f --- /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 000000000000..b5b9c3a98c94 --- /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 + +#include +#include +#include +#include + +#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 000000000000..13e8a8b94cfa --- /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 000000000000..3bf163b6ac49 --- /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 000000000000..c44e214af26c --- /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 + +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..cbbe1263017d --- /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 +#include + +#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 000000000000..87605a9108ae --- /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 + +#include +#include +#include +#include + +#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 000000000000..d26e93f531af --- /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 000000000000..4d5b38df437f --- /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 000000000000..b95bb4fce6ab --- /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 + +#include +#include +#include +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..6215b250047d --- /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 000000000000..b8c2696bc150 --- /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 +#include + +#include +#include +#include +#include +#include + +#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 000000000000..9b72a04701c0 --- /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 + +#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 000000000000..c9740f2dd3a7 --- /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 000000000000..6f26665b30b7 --- /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 + +#include +#include +#include +#include +#include + +#include + +#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 000000000000..b82bf5a1676e --- /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 +# 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 000000000000..31e7800e8074 --- /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 000000000000..3bc2cb1dc869 --- /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 + +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..f7b231f73ac8 --- /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 +#include + +#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 000000000000..1a0de5dea277 --- /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 + +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..1d5a1b9c6f91 --- /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 +#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< + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +/* 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 000000000000..6fbc4a2dd9dd --- /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 +# 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< 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 000000000000..f83d82ecd4bf --- /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 000000000000..fa455844f4e3 --- /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 + +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..fa812d4cc6dc --- /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 +#include + +#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 000000000000..7e3c9f4d2df2 --- /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 + +#include +#include +#include +#include + +#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 000000000000..ec8b175755be --- /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 000000000000..0af95d1a8f5d --- /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 + +#include +#include +#include +#include + +#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 000000000000..4625ba91f609 --- /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 000000000000..7a4016a80fd2 --- /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 000000000000..ee591bc1185e --- /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 + +#include +#include +#include +#include + +#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 000000000000..e8385648e925 --- /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 000000000000..8a10dceae32a --- /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 + +#include +#include +#include +#include +#include +#include + +#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 000000000000..3c7b27f9e5dc --- /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 000000000000..898e1e74d9e7 --- /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 000000000000..ae152bbc6a3d --- /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 000000000000..3f5ff6d1b991 --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 000000000000..9629649cd410 --- /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 +#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< 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 000000000000..fee4c58b8464 --- /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 + +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..18cc7d1e14f1 --- /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 +#include + +#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 000000000000..07f1069f9ac6 --- /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 + +#include +#include +#include +#include + +#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 000000000000..858bec1c4596 --- /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 000000000000..e67a791a1e69 --- /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 000000000000..d35273003415 --- /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 000000000000..1fb79e490261 --- /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 + +#include +#include + +#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 "); +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 000000000000..44c42efb04e2 --- /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 +#include + +#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 000000000000..0f146371b9a0 --- /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 + +#include +#include +#include +#include +#include +#include + +#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 000000000000..e1db79129175 --- /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 000000000000..063bd193709e --- /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 000000000000..957b9f92f760 --- /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 + +#include +#include +#include +#include + +#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 000000000000..acf880136663 --- /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 000000000000..13a23802b31a --- /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 000000000000..d8c4b1c6b153 --- /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 000000000000..261c0cbd9d0a --- /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 + +#include +#include + +#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 "); +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 000000000000..cbd2a01ddb41 --- /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 +#include + +#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 000000000000..0bb4567091cc --- /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 + +#include +#include +#include +#include +#include +#include + +#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 000000000000..2a3b005b67d4 --- /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 000000000000..b9a619d31c2c --- /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 000000000000..ff8ca1b8b7f3 --- /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 + +#include +#include +#include +#include + +#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 000000000000..9ddd93d26f15 --- /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 000000000000..ac94a133abaf --- /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 000000000000..5f4ba5dfa860 --- /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 + +#include +#include +#include +#include +#include +#include + +#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 000000000000..27581251c847 --- /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 000000000000..41095046037a --- /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 000000000000..a73fe4d5b0ff --- /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 000000000000..6e44c3d7a0c7 --- /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 + +#include +#include +#include +#include + +#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 000000000000..572b7196d5a8 --- /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 000000000000..7e2c36b46f56 --- /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 000000000000..180e7f8d2146 --- /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 + +#include +#include +#include +#include + +#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 000000000000..338230052b3c --- /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 000000000000..d1dea1a447f6 --- /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 000000000000..e9b591eaa349 --- /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 000000000000..ef47c384e018 --- /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) + * Author :KG (Krzysztof Gantzke) + */ + +#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 000000000000..382d00fe311d --- /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 + +//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 000000000000..6158ef5b80e6 --- /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) + * Author : KG (Krzysztof Gantzke) + */ + +#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 000000000000..8f62e16c7a37 --- /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 000000000000..25da82883e1f --- /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) + */ + +#ifndef _MEDEVICE_H_ +#define _MEDEVICE_H_ + +#ifndef KBUILD_MODNAME +# define KBUILD_MODNAME KBUILD_STR(memain) +#endif + +#include +//#include +#include +#include + +#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 000000000000..ef4e36955dc8 --- /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 000000000000..091c11e48ed2 --- /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 + +#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 000000000000..f649e3da4f05 --- /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 + +#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 000000000000..4d6ddc8e58a1 --- /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 + +#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 000000000000..02e2408ce5f3 --- /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) + * Author: Krzysztof Gantzke + */ + +#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 000000000000..6a9f08d50bb1 --- /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 +#include + +#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 "); +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 000000000000..717000ff6c1c --- /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) + */ + +#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 000000000000..9eda4bf907ba --- /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) + * Author : KG (Krzysztof Gantzke) + */ + +#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 000000000000..c07d202e8cb5 --- /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 +#include + +#include + +#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 000000000000..a2685080c97b --- /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 000000000000..b3e757cbdda6 --- /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) + */ + +#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 000000000000..8d126b4905a7 --- /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) + */ + +#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 000000000000..6dc719fba57c --- /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) + */ + +#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 000000000000..6cdeb8582453 --- /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 +#include +//#include +#include +#include +#include +#include + +#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 & Krzysztof Gantzke "); +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 000000000000..7616ff7f65cb --- /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) + */ + +#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 000000000000..1868614dc232 --- /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 000000000000..7e8b66c05f7e --- /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 000000000000..d26c89693d2c --- /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 + +#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 000000000000..5230b89b45b5 --- /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 + +#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 000000000000..f42b25c3f622 --- /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 + +#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 000000000000..98d4f1f7a824 --- /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 + +#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 000000000000..19ec2b5d96f0 --- /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 + +#include "metypes.h" +#include "meioctl.h" +#include "meslock.h" + +# include + +#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 000000000000..e48632ddc1aa --- /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 + +#include +#include + +#include +#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 "); +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 000000000000..3c3702cc72eb --- /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 +#include + +#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 000000000000..f1d65d889e23 --- /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 + +#include +#include +#include +#include + +#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 000000000000..80c8af9a8c5a --- /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 000000000000..1a2cab778a12 --- /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 000000000000..228ea15753ea --- /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) + */ + +#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 -- cgit v1.2.3-59-g8ed1b From 18223a99e60787ce41159ed321c8f0a21c328ac1 Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Thu, 20 Nov 2008 21:46:45 +0530 Subject: Staging: meilhaus: fix __symbol_get problems next-20081120 kernel randconfig on x86_64 box fails, while !CONFIG_MODULES drivers/staging/meilhaus/memain.c: In function 'me_probe_pci': drivers/staging/meilhaus/memain.c:425: error: implicit declaration of function '__symbol_get' drivers/staging/meilhaus/memain.c:425: warning: cast to pointer from integer of different size drivers/staging/meilhaus/memain.c:433: warning: cast to pointer from integer of different size drivers/staging/meilhaus/memain.c:453: error: implicit declaration of function '__symbol_put' make[3]: *** [drivers/staging/meilhaus/memain.o] Error 1 the driver uses __symbol_get and __symbol_put instead of marco's symbol_get and symbol_put, I have only build tested the patch. Signed-off-by: Kamalesh Babulal Signed-off-by: Greg Kroah-Hartman --- drivers/staging/meilhaus/memain.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/staging/meilhaus') diff --git a/drivers/staging/meilhaus/memain.c b/drivers/staging/meilhaus/memain.c index 6cdeb8582453..b09d1a6c766c 100644 --- a/drivers/staging/meilhaus/memain.c +++ b/drivers/staging/meilhaus/memain.c @@ -422,7 +422,7 @@ static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id) PDEBUG("module_name: %s\n", module_name); if ((constructor = - (me_pci_constructor_t) __symbol_get(constructor_name)) == NULL) { + (me_pci_constructor_t) symbol_get(constructor_name)) == NULL) { if (request_module(module_name)) { PERROR("Error while request for module %s.\n", module_name); @@ -430,7 +430,7 @@ static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id) } if ((constructor = - (me_pci_constructor_t) __symbol_get(constructor_name)) == + (me_pci_constructor_t) symbol_get(constructor_name)) == NULL) { PERROR("Can't get %s driver module constructor.\n", module_name); @@ -441,7 +441,7 @@ static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id) 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); + symbol_put(constructor_name); PERROR ("Can't get device instance of %s driver module.\n", module_name); @@ -450,7 +450,7 @@ static int me_probe_pci(struct pci_dev *dev, const struct pci_device_id *id) } else { #endif if ((n_device = (*constructor) (dev)) == NULL) { - __symbol_put(constructor_name); + symbol_put(constructor_name); PERROR ("Can't get device instance of %s driver module.\n", module_name); @@ -503,7 +503,7 @@ static void release_instance(me_device_t * device) if (plugged != ME_PLUGGED_IN) { PDEBUG("release: medummy_constructor\n"); - __symbol_put("medummy_constructor"); + symbol_put("medummy_constructor"); } else { if ((dev_id & 0xF000) == 0x6000) { // Exceptions: me61xx, me62xx, me63xx are handled by one driver. dev_id &= 0xF0FF; @@ -513,7 +513,7 @@ static void release_instance(me_device_t * device) constructor_name[3] += (char)((dev_id >> 8) & 0x000F); PDEBUG("release: %s\n", constructor_name); - __symbol_put(constructor_name); + symbol_put(constructor_name); } } -- cgit v1.2.3-59-g8ed1b From d599edcaea987e233fad808f88850f725e8a5530 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 7 Jan 2009 14:31:57 -0800 Subject: staging: __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Signed-off-by: Linus Torvalds --- drivers/staging/comedi/drivers.c | 2 +- drivers/staging/epl/Edrv8139.c | 60 +++++++++++----------- drivers/staging/epl/EplSdoAsySequ.c | 2 +- drivers/staging/frontier/alphatrack.c | 12 ++--- drivers/staging/frontier/tranzport.c | 22 ++++---- drivers/staging/meilhaus/me0600_ext_irq.c | 4 +- drivers/staging/meilhaus/me1400_ext_irq.c | 2 +- drivers/staging/meilhaus/me1600_ao.c | 4 +- drivers/staging/meilhaus/me4600_ai.c | 8 +-- drivers/staging/meilhaus/me4600_ao.c | 6 +-- drivers/staging/meilhaus/me4600_ext_irq.c | 2 +- drivers/staging/meilhaus/me6000_ao.c | 20 ++++---- drivers/staging/meilhaus/me8100_di.c | 6 +-- drivers/staging/meilhaus/me8200_di.c | 4 +- drivers/staging/meilhaus/me8200_do.c | 2 +- drivers/staging/meilhaus/medebug.h | 10 ++-- drivers/staging/otus/80211core/cagg.c | 2 +- drivers/staging/otus/80211core/pub_zfw.h | 2 +- drivers/staging/otus/80211core/struct.h | 2 +- drivers/staging/otus/oal_marc.h | 12 ++--- drivers/staging/rt2860/2860_main_dev.c | 2 +- drivers/staging/rt2860/common/ba_action.c | 18 +++---- drivers/staging/rt2860/common/cmm_data.c | 2 +- drivers/staging/rt2860/common/cmm_data_2860.c | 6 +-- drivers/staging/rt2860/common/cmm_info.c | 2 +- drivers/staging/rt2860/common/dfs.c | 4 +- drivers/staging/rt2860/common/rtmp_init.c | 18 +++---- drivers/staging/rt2860/common/spectrum.c | 44 ++++++++-------- drivers/staging/rt2860/rt_ate.c | 32 ++++++------ drivers/staging/rt2860/rt_linux.c | 18 +++---- drivers/staging/rt2860/rt_linux.h | 2 +- drivers/staging/rt2860/rt_profile.c | 12 ++--- drivers/staging/rt2860/rtmp_def.h | 2 +- drivers/staging/rt2860/sta_ioctl.c | 36 ++++++------- drivers/staging/rt2870/2870_main_dev.c | 6 +-- drivers/staging/rt2870/common/2870_rtmp_init.c | 4 +- drivers/staging/rt2870/common/ba_action.c | 18 +++---- drivers/staging/rt2870/common/cmm_data.c | 2 +- drivers/staging/rt2870/common/cmm_info.c | 2 +- drivers/staging/rt2870/common/dfs.c | 4 +- drivers/staging/rt2870/common/rtmp_init.c | 20 ++++---- drivers/staging/rt2870/common/rtusb_bulk.c | 4 +- drivers/staging/rt2870/common/spectrum.c | 44 ++++++++-------- drivers/staging/rt2870/rt_ate.c | 30 +++++------ drivers/staging/rt2870/rt_linux.c | 18 +++---- drivers/staging/rt2870/rt_linux.h | 2 +- drivers/staging/rt2870/rt_main_dev.c | 2 +- drivers/staging/rt2870/rt_profile.c | 12 ++--- drivers/staging/rt2870/rtmp_def.h | 2 +- drivers/staging/rt2870/sta_ioctl.c | 36 ++++++------- drivers/staging/rt2870/tmp60 | 36 ++++++------- drivers/staging/rt2870/tmp61 | 36 ++++++------- drivers/staging/rtl8187se/ieee80211.h | 2 +- drivers/staging/rtl8187se/ieee80211/ieee80211.h | 2 +- .../rtl8187se/ieee80211/ieee80211_crypt_ccmp.c | 2 +- .../rtl8187se/ieee80211/ieee80211_crypt_tkip.c | 2 +- .../rtl8187se/ieee80211/ieee80211_crypt_wep.c | 2 +- drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c | 2 +- .../rtl8187se/ieee80211/ieee80211_softmac.c | 8 +-- .../rtl8187se/ieee80211/ieee80211_softmac_wx.c | 2 +- drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c | 8 +-- drivers/staging/rtl8187se/r8180_core.c | 12 ++--- drivers/staging/rtl8187se/r8180_rtl8225z2.c | 4 +- drivers/staging/rtl8187se/r8180_wx.c | 14 ++--- 64 files changed, 360 insertions(+), 360 deletions(-) (limited to 'drivers/staging/meilhaus') diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c index 06372b227bb2..36a93b95e3f2 100644 --- a/drivers/staging/comedi/drivers.c +++ b/drivers/staging/comedi/drivers.c @@ -557,7 +557,7 @@ unsigned int comedi_buf_munge(comedi_async * async, unsigned int num_bytes) block_size = num_bytes - count; if (block_size < 0) { rt_printk("%s: %s: bug! block_size is negative\n", - __FILE__, __FUNCTION__); + __FILE__, __func__); break; } if ((int)(async->munge_ptr + block_size - diff --git a/drivers/staging/epl/Edrv8139.c b/drivers/staging/epl/Edrv8139.c index 88ab4a4f1023..296354aaa9c7 100644 --- a/drivers/staging/epl/Edrv8139.c +++ b/drivers/staging/epl/Edrv8139.c @@ -391,19 +391,19 @@ tEplKernel EdrvInit(tEdrvInitParam * pEdrvInitParam_p) // register PCI driver iResult = pci_register_driver(&EdrvDriver); if (iResult != 0) { - printk("%s pci_register_driver failed with %d\n", __FUNCTION__, + printk("%s pci_register_driver failed with %d\n", __func__, iResult); Ret = kEplNoResource; goto Exit; } if (EdrvInstance_l.m_pPciDev == NULL) { - printk("%s m_pPciDev=NULL\n", __FUNCTION__); + printk("%s m_pPciDev=NULL\n", __func__); Ret = kEplNoResource; goto Exit; } // read MAC address from controller - printk("%s local MAC = ", __FUNCTION__); + printk("%s local MAC = ", __func__); for (iResult = 0; iResult < 6; iResult++) { pEdrvInitParam_p->m_abMyMacAddr[iResult] = EDRV_REGB_READ((EDRV_REGDW_IDR0 + iResult)); @@ -434,7 +434,7 @@ tEplKernel EdrvShutdown(void) { // unregister PCI driver - printk("%s calling pci_unregister_driver()\n", __FUNCTION__); + printk("%s calling pci_unregister_driver()\n", __func__); pci_unregister_driver(&EdrvDriver); return kEplSuccessful; @@ -621,7 +621,7 @@ tEplKernel EdrvSendTxMsg(tEdrvTxBuffer * pBuffer_p) EDRV_REGDW_READ((EDRV_REGDW_TSD0 + (EdrvInstance_l.m_uiCurTxDesc * sizeof(DWORD)))); - printk("%s InvOp TSD%u = 0x%08lX", __FUNCTION__, + printk("%s InvOp TSD%u = 0x%08lX", __func__, EdrvInstance_l.m_uiCurTxDesc, dwTemp); printk(" Cmd = 0x%02X\n", (WORD) EDRV_REGB_READ(EDRV_REGB_COMMAND)); @@ -646,7 +646,7 @@ tEplKernel EdrvSendTxMsg(tEdrvTxBuffer * pBuffer_p) dwTemp = EDRV_REGDW_READ((EDRV_REGDW_TSAD0 + (EdrvInstance_l.m_uiCurTxDesc * sizeof(DWORD)))); -// printk("%s TSAD%u = 0x%08lX", __FUNCTION__, EdrvInstance_l.m_uiCurTxDesc, dwTemp); +// printk("%s TSAD%u = 0x%08lX", __func__, EdrvInstance_l.m_uiCurTxDesc, dwTemp); // start transmission EDRV_REGDW_WRITE((EDRV_REGDW_TSD0 + @@ -786,7 +786,7 @@ static int TgtEthIsr(int nIrqNum_p, void *ppDevInstData_p, if (EdrvInstance_l.m_pbTxBuf == NULL) { printk("%s Tx buffers currently not allocated\n", - __FUNCTION__); + __func__); goto Exit; } // read transmit status @@ -842,7 +842,7 @@ static int TgtEthIsr(int nIrqNum_p, void *ppDevInstData_p, if (EdrvInstance_l.m_pbRxBuf == NULL) { printk("%s Rx buffers currently not allocated\n", - __FUNCTION__); + __func__); goto Exit; } // read current offset in receive buffer @@ -944,7 +944,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) DWORD dwTemp; if (EdrvInstance_l.m_pPciDev != NULL) { // Edrv is already connected to a PCI device - printk("%s device %s discarded\n", __FUNCTION__, + printk("%s device %s discarded\n", __func__, pci_name(pPciDev)); iResult = -ENODEV; goto Exit; @@ -953,7 +953,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) if (pPciDev->revision >= 0x20) { printk ("%s device %s is an enhanced 8139C+ version, which is not supported\n", - __FUNCTION__, pci_name(pPciDev)); + __func__, pci_name(pPciDev)); iResult = -ENODEV; goto Exit; } @@ -961,7 +961,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) EdrvInstance_l.m_pPciDev = pPciDev; // enable device - printk("%s enable device\n", __FUNCTION__); + printk("%s enable device\n", __func__); iResult = pci_enable_device(pPciDev); if (iResult != 0) { goto Exit; @@ -972,13 +972,13 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) goto Exit; } - printk("%s request regions\n", __FUNCTION__); + printk("%s request regions\n", __func__); iResult = pci_request_regions(pPciDev, DRV_NAME); if (iResult != 0) { goto Exit; } - printk("%s ioremap\n", __FUNCTION__); + printk("%s ioremap\n", __func__); EdrvInstance_l.m_pIoAddr = ioremap(pci_resource_start(pPciDev, 1), pci_resource_len(pPciDev, 1)); @@ -987,11 +987,11 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) goto Exit; } // enable PCI busmaster - printk("%s enable busmaster\n", __FUNCTION__); + printk("%s enable busmaster\n", __func__); pci_set_master(pPciDev); // reset controller - printk("%s reset controller\n", __FUNCTION__); + printk("%s reset controller\n", __func__); EDRV_REGB_WRITE(EDRV_REGB_COMMAND, EDRV_REGB_COMMAND_RST); // wait until reset has finished @@ -1008,20 +1008,20 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) dwTemp = EDRV_REGDW_READ(EDRV_REGDW_TCR); if (((dwTemp & EDRV_REGDW_TCR_VER_MASK) != EDRV_REGDW_TCR_VER_C) && ((dwTemp & EDRV_REGDW_TCR_VER_MASK) != EDRV_REGDW_TCR_VER_D)) { // unsupported chip - printk("%s Unsupported chip! TCR = 0x%08lX\n", __FUNCTION__, + printk("%s Unsupported chip! TCR = 0x%08lX\n", __func__, dwTemp); iResult = -ENODEV; goto Exit; } // disable interrupts - printk("%s disable interrupts\n", __FUNCTION__); + printk("%s disable interrupts\n", __func__); EDRV_REGW_WRITE(EDRV_REGW_INT_MASK, 0); // acknowledge all pending interrupts EDRV_REGW_WRITE(EDRV_REGW_INT_STATUS, EDRV_REGW_READ(EDRV_REGW_INT_STATUS)); // install interrupt handler - printk("%s install interrupt handler\n", __FUNCTION__); + printk("%s install interrupt handler\n", __func__); iResult = request_irq(pPciDev->irq, TgtEthIsr, IRQF_SHARED, DRV_NAME /*pPciDev->dev.name */ , pPciDev); @@ -1031,16 +1031,16 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) /* // unlock configuration registers - printk("%s unlock configuration registers\n", __FUNCTION__); + printk("%s unlock configuration registers\n", __func__); EDRV_REGB_WRITE(EDRV_REGB_CMD9346, EDRV_REGB_CMD9346_UNLOCK); // check if user specified a MAC address - printk("%s check specified MAC address\n", __FUNCTION__); + printk("%s check specified MAC address\n", __func__); for (iResult = 0; iResult < 6; iResult++) { if (EdrvInstance_l.m_InitParam.m_abMyMacAddr[iResult] != 0) { - printk("%s set local MAC address\n", __FUNCTION__); + printk("%s set local MAC address\n", __func__); // write this MAC address to controller EDRV_REGDW_WRITE(EDRV_REGDW_IDR0, le32_to_cpu(*((DWORD*)&EdrvInstance_l.m_InitParam.m_abMyMacAddr[0]))); @@ -1059,7 +1059,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) */ // allocate buffers - printk("%s allocate buffers\n", __FUNCTION__); + printk("%s allocate buffers\n", __func__); EdrvInstance_l.m_pbTxBuf = pci_alloc_consistent(pPciDev, EDRV_TX_BUFFER_SIZE, &EdrvInstance_l.m_pTxBufDma); @@ -1076,7 +1076,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) goto Exit; } // reset pointers for Tx buffers - printk("%s reset pointers fo Tx buffers\n", __FUNCTION__); + printk("%s reset pointers fo Tx buffers\n", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_TSAD0, 0); dwTemp = EDRV_REGDW_READ(EDRV_REGDW_TSAD0); EDRV_REGDW_WRITE(EDRV_REGDW_TSAD1, 0); @@ -1090,11 +1090,11 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) (WORD) EDRV_REGB_READ(EDRV_REGB_COMMAND)); // set pointer for receive buffer in controller - printk("%s set pointer to Rx buffer\n", __FUNCTION__); + printk("%s set pointer to Rx buffer\n", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_RBSTART, EdrvInstance_l.m_pRxBufDma); // enable transmitter and receiver - printk("%s enable Tx and Rx", __FUNCTION__); + printk("%s enable Tx and Rx", __func__); EDRV_REGB_WRITE(EDRV_REGB_COMMAND, (EDRV_REGB_COMMAND_RE | EDRV_REGB_COMMAND_TE)); printk(" Command = 0x%02X\n", @@ -1104,12 +1104,12 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) EDRV_REGDW_WRITE(EDRV_REGDW_MPC, 0); // set transmit configuration register - printk("%s set Tx conf register", __FUNCTION__); + printk("%s set Tx conf register", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_TCR, EDRV_REGDW_TCR_DEF); printk(" = 0x%08X\n", EDRV_REGDW_READ(EDRV_REGDW_TCR)); // set receive configuration register - printk("%s set Rx conf register", __FUNCTION__); + printk("%s set Rx conf register", __func__); EDRV_REGDW_WRITE(EDRV_REGDW_RCR, EDRV_REGDW_RCR_DEF); printk(" = 0x%08X\n", EDRV_REGDW_READ(EDRV_REGDW_RCR)); @@ -1121,7 +1121,7 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) /* // enable transmitter and receiver - printk("%s enable Tx and Rx", __FUNCTION__); + printk("%s enable Tx and Rx", __func__); EDRV_REGB_WRITE(EDRV_REGB_COMMAND, (EDRV_REGB_COMMAND_RE | EDRV_REGB_COMMAND_TE)); printk(" Command = 0x%02X\n", (WORD) EDRV_REGB_READ(EDRV_REGB_COMMAND)); */ @@ -1129,11 +1129,11 @@ static int EdrvInitOne(struct pci_dev *pPciDev, const struct pci_device_id *pId) EDRV_REGW_WRITE(EDRV_REGW_MULINT, 0); // enable interrupts - printk("%s enable interrupts\n", __FUNCTION__); + printk("%s enable interrupts\n", __func__); EDRV_REGW_WRITE(EDRV_REGW_INT_MASK, EDRV_REGW_INT_MASK_DEF); Exit: - printk("%s finished with %d\n", __FUNCTION__, iResult); + printk("%s finished with %d\n", __func__, iResult); return iResult; } diff --git a/drivers/staging/epl/EplSdoAsySequ.c b/drivers/staging/epl/EplSdoAsySequ.c index 991c6be880c0..6b6a9975d78b 100644 --- a/drivers/staging/epl/EplSdoAsySequ.c +++ b/drivers/staging/epl/EplSdoAsySequ.c @@ -876,7 +876,7 @@ static tEplKernel EplSdoAsySeqProcess(unsigned int uiHandle_p, { /* PRINTF3("%s scon=%u rcon=%u\n", - __FUNCTION__, + __func__, pRecFrame_p->m_le_bSendSeqNumCon, pRecFrame_p->m_le_bRecSeqNumCon); */ diff --git a/drivers/staging/frontier/alphatrack.c b/drivers/staging/frontier/alphatrack.c index 61d7c5df87af..6136e3f8762d 100644 --- a/drivers/staging/frontier/alphatrack.c +++ b/drivers/staging/frontier/alphatrack.c @@ -239,7 +239,7 @@ static void usb_alphatrack_interrupt_in_callback(struct urb *urb) goto exit; } else { dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); goto resubmit; /* maybe we can recover */ } } @@ -261,7 +261,7 @@ static void usb_alphatrack_interrupt_in_callback(struct urb *urb) if(dev->offline > 0 && dev->interrupt_in_buffer[1] != 0xff) { dev->offline = 0; } if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; } #endif - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); next_ring_head = (dev->ring_head+1) % ring_buffer_size; if (next_ring_head != dev->ring_tail) { @@ -305,7 +305,7 @@ static void usb_alphatrack_interrupt_out_callback(struct urb *urb) urb->status == -ESHUTDOWN)) dbg_info(&dev->intf->dev, "%s - nonzero write interrupt status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); atomic_dec(&dev->writes_pending); dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); @@ -330,7 +330,7 @@ static int usb_alphatrack_open(struct inode *inode, struct file *file) if (!interface) { err("%s - error, can't find device for minor %d\n", - __FUNCTION__, subminor); + __func__, subminor); retval = -ENODEV; goto unlock_disconnect_exit; } @@ -514,7 +514,7 @@ static ssize_t usb_alphatrack_read(struct file *file, char __user *buffer, size_ } dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; c+=INPUT_CMD_SIZE; - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); } retval = c; @@ -573,7 +573,7 @@ static ssize_t usb_alphatrack_write(struct file *file, const char __user *buffer if (bytes_to_write < count) dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); - dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write); + dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; diff --git a/drivers/staging/frontier/tranzport.c b/drivers/staging/frontier/tranzport.c index 275faa762388..79abb6b16f74 100644 --- a/drivers/staging/frontier/tranzport.c +++ b/drivers/staging/frontier/tranzport.c @@ -335,7 +335,7 @@ static void usb_tranzport_interrupt_in_callback(struct urb *urb) goto exit; } else { dbg_info(&dev->intf->dev, "%s: nonzero status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); goto resubmit; /* maybe we can recover */ } } @@ -345,7 +345,7 @@ static void usb_tranzport_interrupt_in_callback(struct urb *urb) "Urb length was %d bytes!! Do something intelligent \n", urb->actual_length); } else { dbg_info(&dev->intf->dev, "%s: received: %02x%02x%02x%02x%02x%02x%02x%02x\n", - __FUNCTION__, dev->interrupt_in_buffer[0],dev->interrupt_in_buffer[1],dev->interrupt_in_buffer[2],dev->interrupt_in_buffer[3],dev->interrupt_in_buffer[4],dev->interrupt_in_buffer[5],dev->interrupt_in_buffer[6],dev->interrupt_in_buffer[7]); + __func__, dev->interrupt_in_buffer[0],dev->interrupt_in_buffer[1],dev->interrupt_in_buffer[2],dev->interrupt_in_buffer[3],dev->interrupt_in_buffer[4],dev->interrupt_in_buffer[5],dev->interrupt_in_buffer[6],dev->interrupt_in_buffer[7]); #if SUPPRESS_EXTRA_OFFLINE_EVENTS if(dev->offline == 2 && dev->interrupt_in_buffer[1] == 0xff) { goto resubmit; } if(dev->offline == 1 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 2; goto resubmit; } @@ -355,7 +355,7 @@ static void usb_tranzport_interrupt_in_callback(struct urb *urb) if(dev->offline == 0 && dev->interrupt_in_buffer[1] == 0xff) { dev->offline = 1; } #endif - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); next_ring_head = (dev->ring_head+1) % ring_buffer_size; @@ -399,7 +399,7 @@ static void usb_tranzport_interrupt_out_callback(struct urb *urb) urb->status == -ESHUTDOWN)) dbg_info(&dev->intf->dev, "%s - nonzero write interrupt status received: %d\n", - __FUNCTION__, urb->status); + __func__, urb->status); dev->interrupt_out_busy = 0; wake_up_interruptible(&dev->write_wait); @@ -424,7 +424,7 @@ static int usb_tranzport_open(struct inode *inode, struct file *file) if (!interface) { err("%s - error, can't find device for minor %d\n", - __FUNCTION__, subminor); + __func__, subminor); retval = -ENODEV; goto unlock_disconnect_exit; } @@ -613,7 +613,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t } dbg_info(&dev->intf->dev, "%s: copying to userspace: %02x%02x%02x%02x%02x%02x%02x%02x\n", - __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); + __func__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); #if BUFFERED_READS c = 0; @@ -632,7 +632,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t // FIXME the math is wrong for going in reverse, actually, as the midi spec doesn't allow signed chars dbg_info(&dev->intf->dev, "%s: trying to compress: %02x%02x%02x%02x%02x %02x %02x %02x\n", - __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); + __func__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); if(((*dev->ring_buffer)[dev->ring_tail].cmd[6] != 0 && @@ -645,7 +645,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t ((*dev->ring_buffer)[dev->ring_tail].cmd[5] == (*dev->ring_buffer)[next_tail].cmd[5])) { dbg_info(&dev->intf->dev, "%s: should compress: %02x%02x%02x%02x%02x%02x%02x%02x\n", - __FUNCTION__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); + __func__, (*dev->ring_buffer)[dev->ring_tail].cmd[0],(*dev->ring_buffer)[dev->ring_tail].cmd[1],(*dev->ring_buffer)[dev->ring_tail].cmd[2],(*dev->ring_buffer)[dev->ring_tail].cmd[3],(*dev->ring_buffer)[dev->ring_tail].cmd[4],(*dev->ring_buffer)[dev->ring_tail].cmd[5],(*dev->ring_buffer)[dev->ring_tail].cmd[6],(*dev->ring_buffer)[dev->ring_tail].cmd[7]); newwheel += oldwheel; if(oldwheel > 0 && !(newwheel > 0)) { @@ -673,7 +673,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; c+=8; - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); } retval = c; @@ -684,7 +684,7 @@ static ssize_t usb_tranzport_read(struct file *file, char __user *buffer, size_t } dev->ring_tail = (dev->ring_tail+1) % ring_buffer_size; - dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __FUNCTION__,dev->ring_head,dev->ring_tail); + dbg_info(&dev->intf->dev, "%s: head, tail are %x, %x\n", __func__,dev->ring_head,dev->ring_tail); retval = 8; #endif /* BUFFERED_READS */ @@ -743,7 +743,7 @@ static ssize_t usb_tranzport_write(struct file *file, const char __user *buffer, if (bytes_to_write < count) dev_warn(&dev->intf->dev, "Write buffer overflow, %zd bytes dropped\n",count-bytes_to_write); - dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __FUNCTION__, count, bytes_to_write); + dbg_info(&dev->intf->dev, "%s: count = %zd, bytes_to_write = %zd\n", __func__, count, bytes_to_write); if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write)) { retval = -EFAULT; diff --git a/drivers/staging/meilhaus/me0600_ext_irq.c b/drivers/staging/meilhaus/me0600_ext_irq.c index a449ab200940..eba18adecb72 100644 --- a/drivers/staging/meilhaus/me0600_ext_irq.c +++ b/drivers/staging/meilhaus/me0600_ext_irq.c @@ -360,7 +360,7 @@ static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs) if (instance->lintno > 1) { PERROR_CRITICAL ("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n", - __FUNCTION__, instance->lintno, inl(instance->intcsr)); + __func__, instance->lintno, inl(instance->intcsr)); return IRQ_NONE; } @@ -384,7 +384,7 @@ static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs) } else { PINFO ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, status); + jiffies, __func__, status); ret = IRQ_NONE; } spin_unlock(instance->intcsr_lock); diff --git a/drivers/staging/meilhaus/me1400_ext_irq.c b/drivers/staging/meilhaus/me1400_ext_irq.c index b8c2696bc150..b4df7cc58ab7 100644 --- a/drivers/staging/meilhaus/me1400_ext_irq.c +++ b/drivers/staging/meilhaus/me1400_ext_irq.c @@ -349,7 +349,7 @@ static irqreturn_t me1400_ext_irq_isr(int irq, void *dev_id, (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); + jiffies, __func__, status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me1600_ao.c b/drivers/staging/meilhaus/me1600_ao.c index 6f26665b30b7..d127c6b00307 100644 --- a/drivers/staging/meilhaus/me1600_ao.c +++ b/drivers/staging/meilhaus/me1600_ao.c @@ -977,7 +977,7 @@ static void me1600_ao_work_control_task(struct work_struct *work) container_of((void *)work, me1600_ao_subdevice_t, ao_control_task); #endif - PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies, + PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, instance->ao_idx); if (!((instance->ao_regs_shadows)->trigger & instance->ao_idx)) { // Output was triggerd. @@ -1027,7 +1027,7 @@ static void me1600_ao_work_control_task(struct work_struct *work) queue_delayed_work(instance->me1600_workqueue, &instance->ao_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me4600_ai.c b/drivers/staging/meilhaus/me4600_ai.c index 1a0de5dea277..0a8c9d737e90 100644 --- a/drivers/staging/meilhaus/me4600_ai.c +++ b/drivers/staging/meilhaus/me4600_ai.c @@ -2629,11 +2629,11 @@ static irqreturn_t me4600_ai_isr(int irq, void *dev_id, struct pt_regs *regs) 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__); + jiffies, __func__); } else { PINFO ("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, irq_status); + jiffies, __func__, irq_status); } #endif return IRQ_NONE; @@ -3329,7 +3329,7 @@ static void me4600_ai_work_control_task(struct work_struct *work) instance = container_of((void *)work, me4600_ai_subdevice_t, ai_control_task); #endif - PINFO("<%s: %ld> executed.\n", __FUNCTION__, jiffies); + PINFO("<%s: %ld> executed.\n", __func__, jiffies); status = inl(instance->status_reg); PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, @@ -3428,7 +3428,7 @@ static void me4600_ai_work_control_task(struct work_struct *work) queue_delayed_work(instance->me4600_workqueue, &instance->ai_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me4600_ao.c b/drivers/staging/meilhaus/me4600_ao.c index 2c92e655a81e..e2bec8229abd 100644 --- a/drivers/staging/meilhaus/me4600_ao.c +++ b/drivers/staging/meilhaus/me4600_ao.c @@ -2294,7 +2294,7 @@ static irqreturn_t me4600_ao_isr(int irq, void *dev_id 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); + jiffies, __func__, instance->ao_idx, irq_status); return IRQ_NONE; } @@ -3009,7 +3009,7 @@ static void me4600_ao_work_control_task( instance = container_of((void *)work, me4600_ao_subdevice_t, ao_control_task); #endif - PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies, + PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, instance->ao_idx); status = inl(instance->status_reg); @@ -3316,7 +3316,7 @@ static void me4600_ao_work_control_task( queue_delayed_work(instance->me4600_workqueue, &instance->ao_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me4600_ext_irq.c b/drivers/staging/meilhaus/me4600_ext_irq.c index 8a10dceae32a..adc1e1babf40 100644 --- a/drivers/staging/meilhaus/me4600_ext_irq.c +++ b/drivers/staging/meilhaus/me4600_ext_irq.c @@ -356,7 +356,7 @@ static irqreturn_t me4600_ext_irq_isr(int irq, void *dev_id, 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); + jiffies, __func__, irq_status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me6000_ao.c b/drivers/staging/meilhaus/me6000_ao.c index 3f5ff6d1b991..94f01231f79a 100644 --- a/drivers/staging/meilhaus/me6000_ao.c +++ b/drivers/staging/meilhaus/me6000_ao.c @@ -863,7 +863,7 @@ static int me6000_ao_io_single_write(me_subdevice_t * subdevice, /// @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, + PINFO("<%s> start mode= 0x%08x %s\n", __func__, mode, (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) ? "SYNCHRONOUS" : ""); if (instance->fifo & ME6000_AO_HAS_FIFO) { // FIFO - Continous mode @@ -1663,7 +1663,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, status = inl(instance->status_reg); //Start state machine and interrupts - PINFO("<%s:%d> Start state machine.\n", __FUNCTION__, __LINE__); + PINFO("<%s:%d> Start state machine.\n", __func__, __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"); @@ -1671,7 +1671,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, } 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__, + PINFO("<%s:%d> Start interrupts.\n", __func__, __LINE__); ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ; } @@ -1682,7 +1682,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags); //Trigger output - PINFO("<%s> start mode= 0x%x %s\n", __FUNCTION__, instance->start_mode, + PINFO("<%s> start mode= 0x%x %s\n", __func__, instance->start_mode, (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) ? "SYNCHRONOUS" : ""); if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) { //Trigger outputs @@ -1777,7 +1777,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, 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__, + PINFO("<%s:%d> Start interrupts.\n", __func__, __LINE__); ctrl = inl(instance->ctrl_reg); ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ; @@ -1819,7 +1819,7 @@ static int me6000_ao_io_stream_start(me_subdevice_t * subdevice, spin_lock_irqsave(&instance->subdevice_lock, cpu_flags); PINFO("<%s:%d> Start interrupts.\n", - __FUNCTION__, __LINE__); + __func__, __LINE__); ctrl = inl(instance->ctrl_reg); ctrl |= ME6000_AO_CTRL_BIT_ENABLE_IRQ; outl(ctrl, instance->ctrl_reg); @@ -2346,7 +2346,7 @@ static irqreturn_t me6000_ao_isr(int irq, void *dev_id, struct pt_regs *regs) 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); + jiffies, __func__, instance->ao_idx, irq_status); return IRQ_NONE; } @@ -2861,7 +2861,7 @@ int inline ao_stop_immediately(me6000_ao_subdevice_t * instance) } } - PINFO("<%s> Wait for stop: %d\n", __FUNCTION__, i); + PINFO("<%s> Wait for stop: %d\n", __func__, i); //Still working! set_current_state(TASK_INTERRUPTIBLE); @@ -3132,7 +3132,7 @@ static void me6000_ao_work_control_task( instance = container_of((void *)work, me6000_ao_subdevice_t, ao_control_task); #endif - PINFO("<%s: %ld> executed. idx=%d\n", __FUNCTION__, jiffies, + PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies, instance->ao_idx); status = inl(instance->status_reg); @@ -3550,7 +3550,7 @@ static void me6000_ao_work_control_task( queue_delayed_work(instance->me6000_workqueue, &instance->ao_control_task, 1); } else { - PINFO("<%s> Ending control task.\n", __FUNCTION__); + PINFO("<%s> Ending control task.\n", __func__); } } diff --git a/drivers/staging/meilhaus/me8100_di.c b/drivers/staging/meilhaus/me8100_di.c index 0f146371b9a0..971727c9e305 100644 --- a/drivers/staging/meilhaus/me8100_di.c +++ b/drivers/staging/meilhaus/me8100_di.c @@ -536,7 +536,7 @@ static irqreturn_t me8100_isr(int irq, void *dev_id, struct pt_regs *regs) PLX_INTCSR_LOCAL_INT1_EN)) { PINFO ("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, icsr); + jiffies, __func__, icsr); return IRQ_NONE; } } else if (instance->di_idx == 1) { @@ -547,11 +547,11 @@ static irqreturn_t me8100_isr(int irq, void *dev_id, struct pt_regs *regs) PLX_INTCSR_LOCAL_INT2_EN)) { PINFO ("%ld Shared interrupt. %s(): idx=1 plx:irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, icsr); + jiffies, __func__, icsr); return IRQ_NONE; } } else { - PERROR("%s():Wrong interrupt idx=%d csr=0x%X.\n", __FUNCTION__, + PERROR("%s():Wrong interrupt idx=%d csr=0x%X.\n", __func__, instance->di_idx, icsr); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me8200_di.c b/drivers/staging/meilhaus/me8200_di.c index 0bb4567091cc..27525bc067b6 100644 --- a/drivers/staging/meilhaus/me8200_di.c +++ b/drivers/staging/meilhaus/me8200_di.c @@ -570,7 +570,7 @@ static irqreturn_t me8200_isr(int irq, void *dev_id, struct pt_regs *regs) if (!irq_status) { PINFO ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->di_idx, irq_status); + jiffies, __func__, instance->di_idx, irq_status); return IRQ_NONE; } @@ -658,7 +658,7 @@ static irqreturn_t me8200_isr_EX(int irq, void *dev_id, struct pt_regs *regs) if (!irq_status) { PINFO ("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n", - jiffies, __FUNCTION__, instance->di_idx, irq_status); + jiffies, __func__, instance->di_idx, irq_status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/me8200_do.c b/drivers/staging/meilhaus/me8200_do.c index 5f4ba5dfa860..d2bebd16ff41 100644 --- a/drivers/staging/meilhaus/me8200_do.c +++ b/drivers/staging/meilhaus/me8200_do.c @@ -475,7 +475,7 @@ static irqreturn_t me8200_do_isr(int irq, void *dev_id, struct pt_regs *regs) (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); + jiffies, __func__, instance->do_idx, irq_status); return IRQ_NONE; } diff --git a/drivers/staging/meilhaus/medebug.h b/drivers/staging/meilhaus/medebug.h index 382d00fe311d..dcfb97c26fd1 100644 --- a/drivers/staging/meilhaus/medebug.h +++ b/drivers/staging/meilhaus/medebug.h @@ -66,21 +66,21 @@ #ifdef MEDEBUG_DEBUG # define PDEBUG(fmt, args...) \ - printk(KERN_DEBUG"ME_DRV D: <%s> " fmt, __FUNCTION__, ##args) + printk(KERN_DEBUG"ME_DRV D: <%s> " fmt, __func__, ##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) + printk(KERN_DEBUG"ME_DRV L: <%s> " fmt, __func__, ##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) + printk(KERN_DEBUG"ME_DRV R: <%s:%d> REG:" fmt, __func__, __LINE__, ##args) #else # define PDEBUG_REG(fmt, args...) #endif @@ -108,7 +108,7 @@ //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) + printk(KERN_CRIT"ME_DRV SECURITY: <%s:%s:%i> " fmt, __FILE__, __func__, __LINE__, ##args) //This debug is to keep track in customers' system # define PLOG(fmt, args...) \ printk(KERN_INFO"ME_DRV: " fmt, ##args) @@ -116,7 +116,7 @@ //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) + printk(KERN_CRIT"ME_DRV: <%s:%s:%i> " fmt, __FILE__, __func__, __LINE__, ##args) #else # define PDEVELOP(fmt, args...) #endif diff --git a/drivers/staging/otus/80211core/cagg.c b/drivers/staging/otus/80211core/cagg.c index fcfd01a6b36c..4942190747a1 100644 --- a/drivers/staging/otus/80211core/cagg.c +++ b/drivers/staging/otus/80211core/cagg.c @@ -1933,7 +1933,7 @@ u16_t zfAggRx(zdev_t* dev, zbuf_t* buf, struct zsAdditionInfo *addInfo, struct a */ /* zm_msg2_agg(ZM_LV_0, "queue seq = ", seq_no); - * DbgPrint("%s:%s%lxh %s%lxh\n", __FUNCTION__, "queue seq=", seq_no, + * DbgPrint("%s:%s%lxh %s%lxh\n", __func__, "queue seq=", seq_no, * "; seq_start=", tid_rx->seq_start); */ diff --git a/drivers/staging/otus/80211core/pub_zfw.h b/drivers/staging/otus/80211core/pub_zfw.h index 01a227216726..2474bb7536e8 100644 --- a/drivers/staging/otus/80211core/pub_zfw.h +++ b/drivers/staging/otus/80211core/pub_zfw.h @@ -23,7 +23,7 @@ /* Buffer management */ #ifdef ZM_ENABLE_BUFFER_DEBUG extern zbuf_t* zfwBufAllocateWithContext(zdev_t* dev, u16_t len, u8_t *functionName, ULONG line); -#define zfwBufAllocate(dev, len) zfwBufAllocateWithContext(dev, len, (u8_t *)__FUNCTION__, __LINE__) +#define zfwBufAllocate(dev, len) zfwBufAllocateWithContext(dev, len, (u8_t *)__func__, __LINE__) #else extern zbuf_t* zfwBufAllocate(zdev_t* dev, u16_t len); #endif diff --git a/drivers/staging/otus/80211core/struct.h b/drivers/staging/otus/80211core/struct.h index 43631c630a8f..17b5ce37ebb5 100644 --- a/drivers/staging/otus/80211core/struct.h +++ b/drivers/staging/otus/80211core/struct.h @@ -137,7 +137,7 @@ extern const u8_t zg11gRateTbl[8]; #ifdef ZM_ENABLE_BUFFER_TRACE extern void zfwBufTrace(zdev_t* dev, zbuf_t *buf, u8_t *functionName); -#define ZM_BUFFER_TRACE(dev, buf) zfwBufTrace(dev, buf, __FUNCTION__); +#define ZM_BUFFER_TRACE(dev, buf) zfwBufTrace(dev, buf, __func__); #else #define ZM_BUFFER_TRACE(dev, buf) #endif diff --git a/drivers/staging/otus/oal_marc.h b/drivers/staging/otus/oal_marc.h index 438e4bc2e9a6..206111616a03 100644 --- a/drivers/staging/otus/oal_marc.h +++ b/drivers/staging/otus/oal_marc.h @@ -106,14 +106,14 @@ /***** Debug message *****/ #if 0 -#define zm_debug_msg0(msg) printk("%s:%s\n", __FUNCTION__, msg); -#define zm_debug_msg1(msg, val) printk("%s:%s%ld\n", __FUNCTION__, \ +#define zm_debug_msg0(msg) printk("%s:%s\n", __func__, msg); +#define zm_debug_msg1(msg, val) printk("%s:%s%ld\n", __func__, \ msg, (u32_t)val); -#define zm_debug_msg2(msg, val) printk("%s:%s%lxh\n", __FUNCTION__, \ +#define zm_debug_msg2(msg, val) printk("%s:%s%lxh\n", __func__, \ msg, (u32_t)val); -#define zm_debug_msg_s(msg, val) printk("%s:%s%s\n", __FUNCTION__, \ +#define zm_debug_msg_s(msg, val) printk("%s:%s%s\n", __func__, \ msg, val); -#define zm_debug_msg_p(msg, val1, val2) printk("%s:%s%01ld.%02ld\n", __FUNCTION__, \ +#define zm_debug_msg_p(msg, val1, val2) printk("%s:%s%01ld.%02ld\n", __func__, \ msg, (val1/val2), (((val1*100)/val2)%100)); #define zm_dbg(S) printk S #else @@ -127,7 +127,7 @@ #define zm_assert(expr) if(!(expr)) { \ printk( "Atheors Assertion failed! %s,%s,%s,line=%d\n", \ - #expr,__FILE__,__FUNCTION__,__LINE__); \ + #expr,__FILE__,__func__,__LINE__); \ } #define DbgPrint printk diff --git a/drivers/staging/rt2860/2860_main_dev.c b/drivers/staging/rt2860/2860_main_dev.c index 1e38f2d1f692..08ca81f43cc8 100644 --- a/drivers/staging/rt2860/2860_main_dev.c +++ b/drivers/staging/rt2860/2860_main_dev.c @@ -1202,7 +1202,7 @@ VOID RT28xx_UpdateBeaconToAsic( UCHAR bcn_idx = 0; { - DBGPRINT(RT_DEBUG_ERROR, ("%s() : No valid Interface be found.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s() : No valid Interface be found.\n", __func__)); return; } diff --git a/drivers/staging/rt2860/common/ba_action.c b/drivers/staging/rt2860/common/ba_action.c index 8247aeb73a23..591d1e2158da 100644 --- a/drivers/staging/rt2860/common/ba_action.c +++ b/drivers/staging/rt2860/common/ba_action.c @@ -599,7 +599,7 @@ VOID BAOriSessionAdd( pBAEntry->ORIBATimer.TimerValue = 0; //pFrame->TimeOutValue; - DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __FUNCTION__, pEntry->TXBAbitmap, + DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __func__, pEntry->TXBAbitmap, pBAEntry->BAWinSize, pBAEntry->ORIBATimer.TimerValue)); // SEND BAR ; @@ -673,7 +673,7 @@ BOOLEAN BARecSessionAdd( ba_refresh_reordering_mpdus(pAd, pBAEntry); } - DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __FUNCTION__, pAd->BATable.numAsRecipient, Idx, + DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __func__, pAd->BATable.numAsRecipient, Idx, pFrame->BaParm.BufSize, BAWinSize)); // Start fill in parameters. @@ -915,7 +915,7 @@ VOID BAOriSessionTearDown( return; } - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BAOriEntry[Idx]; DBGPRINT(RT_DEBUG_TRACE,("\t===>Idx = %ld, Wcid=%d.TID=%d, ORI_BA_Status = %d \n", Idx, Wcid, TID, pBAEntry->ORI_BA_Status)); @@ -974,7 +974,7 @@ VOID BARecSessionTearDown( if (Idx == 0) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BARecEntry[Idx]; @@ -1185,7 +1185,7 @@ VOID PeerAddBAReqAction( PULONG ptemp; PMAC_TABLE_ENTRY pMacEntry; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __func__, Elem->Wcid)); //hex_dump("AddBAReq", Elem->Msg, Elem->MsgLen); @@ -1269,7 +1269,7 @@ VOID PeerAddBAReqAction( MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); MlmeFreeMemory(pAd, pOutBuffer); - DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __FUNCTION__, Elem->Wcid, ADDframe.BaParm.TID, + DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __func__, Elem->Wcid, ADDframe.BaParm.TID, ADDframe.BaParm.BufSize)); } @@ -1288,7 +1288,7 @@ VOID PeerAddBARspAction( if (Elem->Wcid >= MAX_LEN_OF_MAC_TABLE) return; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __func__, Elem->Wcid)); //hex_dump("PeerAddBARspAction()", Elem->Msg, Elem->MsgLen); @@ -1329,7 +1329,7 @@ VOID PeerDelBAAction( //PUCHAR pOutBuffer = NULL; PFRAME_DELBA_REQ pDelFrame = NULL; - DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __func__)); //DELBA Request from unknown peer, ignore this. if (PeerDelBAActionSanity(pAd, Elem->Wcid, Elem->Msg, Elem->MsgLen)) { @@ -1366,7 +1366,7 @@ BOOLEAN CntlEnqueueForRecv( TID = (UCHAR)pFrame->BARControl.TID; - DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __func__, Wcid, TID)); //hex_dump("BAR", (PCHAR) pFrame, MsgLen); // Do nothing if the driver is starting halt state. // This might happen when timer already been fired before cancel timer with mlmehalt diff --git a/drivers/staging/rt2860/common/cmm_data.c b/drivers/staging/rt2860/common/cmm_data.c index ac549011ccb9..b67b9eba7229 100644 --- a/drivers/staging/rt2860/common/cmm_data.c +++ b/drivers/staging/rt2860/common/cmm_data.c @@ -2787,7 +2787,7 @@ BOOLEAN MacTableDeleteEntry( } else { - printk("\n%s: Impossible Wcid = %d !!!!!\n", __FUNCTION__, wcid); + printk("\n%s: Impossible Wcid = %d !!!!!\n", __func__, wcid); } } diff --git a/drivers/staging/rt2860/common/cmm_data_2860.c b/drivers/staging/rt2860/common/cmm_data_2860.c index 4f414edd658c..419e50c3fc4c 100644 --- a/drivers/staging/rt2860/common/cmm_data_2860.c +++ b/drivers/staging/rt2860/common/cmm_data_2860.c @@ -1002,7 +1002,7 @@ VOID RT28xxPciStaAsicSleepThenAutoWakeup( AutoWakeupCfg.field.AutoLeadTime = 5; RTMP_IO_WRITE32(pAd, AUTO_WAKEUP_CFG, AutoWakeupCfg.word); AsicSendCommandToMcu(pAd, 0x30, 0xff, 0xff, 0x00); // send POWER-SAVE command to MCU. Timeout 40us. - DBGPRINT(RT_DEBUG_TRACE, ("<-- %s, TbttNumToNextWakeUp=%d \n", __FUNCTION__, TbttNumToNextWakeUp)); + DBGPRINT(RT_DEBUG_TRACE, ("<-- %s, TbttNumToNextWakeUp=%d \n", __func__, TbttNumToNextWakeUp)); } OPSTATUS_SET_FLAG(pAd, fOP_STATUS_DOZE); } @@ -1115,7 +1115,7 @@ VOID RT28xxPciMlmeRadioOn( if (!RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF)) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __func__)); if ((pAd->OpMode == OPMODE_AP) || ((pAd->OpMode == OPMODE_STA) && (!OPSTATUS_TEST_FLAG(pAd, fOP_STATUS_ADVANCE_POWER_SAVE_PCIE_DEVICE)))) @@ -1165,7 +1165,7 @@ VOID RT28xxPciMlmeRadioOFF( if (RTMP_TEST_FLAG(pAd, fRTMP_ADAPTER_RADIO_OFF)) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>\n", __func__)); // Set LED RTMPSetLED(pAd, LED_RADIO_OFF); diff --git a/drivers/staging/rt2860/common/cmm_info.c b/drivers/staging/rt2860/common/cmm_info.c index 0aadf8af633a..dd92ac6eaed2 100644 --- a/drivers/staging/rt2860/common/cmm_info.c +++ b/drivers/staging/rt2860/common/cmm_info.c @@ -2039,7 +2039,7 @@ VOID RTMPIoctlGetMacTable( wrq->u.data.length = sizeof(RT_802_11_MAC_TABLE); if (copy_to_user(wrq->u.data.pointer, &MacTab, wrq->u.data.length)) { - DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __func__)); } msg = (CHAR *) kmalloc(sizeof(CHAR)*(MAX_LEN_OF_MAC_TABLE*MAC_LINE_LEN), MEM_ALLOC_FLAG); diff --git a/drivers/staging/rt2860/common/dfs.c b/drivers/staging/rt2860/common/dfs.c index 23cf1510e2d2..b09bba5cb206 100644 --- a/drivers/staging/rt2860/common/dfs.c +++ b/drivers/staging/rt2860/common/dfs.c @@ -428,7 +428,7 @@ INT Set_ChMovingTime_Proc( pAd->CommonCfg.RadarDetect.ChMovingTime = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.ChMovingTime)); return TRUE; @@ -444,7 +444,7 @@ INT Set_LongPulseRadarTh_Proc( pAd->CommonCfg.RadarDetect.LongPulseRadarTh = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.LongPulseRadarTh)); return TRUE; diff --git a/drivers/staging/rt2860/common/rtmp_init.c b/drivers/staging/rt2860/common/rtmp_init.c index 84edfa52dec3..563f2c5bb856 100644 --- a/drivers/staging/rt2860/common/rtmp_init.c +++ b/drivers/staging/rt2860/common/rtmp_init.c @@ -2542,7 +2542,7 @@ NDIS_STATUS NICLoadFirmware( #ifdef BIN_IN_FILE #define NICLF_DEFAULT_USE() \ flg_default_firm_use = TRUE; \ - printk("%s - Use default firmware!\n", __FUNCTION__); + printk("%s - Use default firmware!\n", __func__); NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PUCHAR src; @@ -2557,7 +2557,7 @@ NDIS_STATUS NICLoadFirmware( BOOLEAN flg_default_firm_use = FALSE; - DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __func__)); /* init */ pFirmwareImage = NULL; @@ -2580,7 +2580,7 @@ NDIS_STATUS NICLoadFirmware( if (pFirmwareImage == NULL) { /* allocate fail, use default firmware array in firmware.h */ - printk("%s - Allocate memory fail!\n", __FUNCTION__); + printk("%s - Allocate memory fail!\n", __func__); NICLF_DEFAULT_USE(); } else @@ -2601,7 +2601,7 @@ NDIS_STATUS NICLoadFirmware( if (IS_ERR(srcf)) { printk("%s - Error %ld opening %s\n", - __FUNCTION__, -PTR_ERR(srcf), src); + __func__, -PTR_ERR(srcf), src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2609,7 +2609,7 @@ NDIS_STATUS NICLoadFirmware( /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - printk("%s - %s does not have a write method\n", __FUNCTION__, src); + printk("%s - %s does not have a write method\n", __func__, src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2623,7 +2623,7 @@ NDIS_STATUS NICLoadFirmware( if (FileLength != MAX_FIRMWARE_IMAGE_SIZE) { printk("%s: error file length (=%d) in RT2860AP.BIN\n", - __FUNCTION__, FileLength); + __func__, FileLength); NICLF_DEFAULT_USE(); break; } @@ -2646,7 +2646,7 @@ NDIS_STATUS NICLoadFirmware( /* CRC fail */ printk("%s: CRC = 0x%02x 0x%02x " "error, should be 0x%02x 0x%02x\n", - __FUNCTION__, + __func__, pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-2], pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-1], (UCHAR)(crc>>8), (UCHAR)(crc)); @@ -2665,7 +2665,7 @@ NDIS_STATUS NICLoadFirmware( ((FIRMWARE_MAJOR_VERSION << 8) + FIRMWARE_MINOR_VERSION)) { - printk("%s: firmware version too old!\n", __FUNCTION__); + printk("%s: firmware version too old!\n", __func__); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2770,7 +2770,7 @@ NDIS_STATUS NICLoadFirmware( } /* End of if */ DBGPRINT(RT_DEBUG_TRACE, - ("<=== %s (status=%d)\n", __FUNCTION__, Status)); + ("<=== %s (status=%d)\n", __func__, Status)); return Status; } /* End of NICLoadFirmware */ diff --git a/drivers/staging/rt2860/common/spectrum.c b/drivers/staging/rt2860/common/spectrum.c index 85e636a607f7..0265a6d1df1a 100644 --- a/drivers/staging/rt2860/common/spectrum.c +++ b/drivers/staging/rt2860/common/spectrum.c @@ -49,7 +49,7 @@ VOID MeasureReqTabInit( if (pAd->CommonCfg.pMeasureReqTab) NdisZeroMemory(pAd->CommonCfg.pMeasureReqTab, sizeof(MEASURE_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __func__)); return; } @@ -77,7 +77,7 @@ static PMEASURE_REQ_ENTRY MeasureReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -114,7 +114,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -175,7 +175,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -210,7 +210,7 @@ static VOID MeasureReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return; } @@ -267,7 +267,7 @@ VOID TpcReqTabInit( if (pAd->CommonCfg.pTpcReqTab) NdisZeroMemory(pAd->CommonCfg.pTpcReqTab, sizeof(TPC_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __func__)); return; } @@ -295,7 +295,7 @@ static PTPC_REQ_ENTRY TpcReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -333,7 +333,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -394,7 +394,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -429,7 +429,7 @@ static VOID TpcReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return; } @@ -782,7 +782,7 @@ VOID EnqueueMeasurementReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -844,7 +844,7 @@ VOID EnqueueMeasurementRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -898,7 +898,7 @@ VOID EnqueueTPCReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -950,7 +950,7 @@ VOID EnqueueTPCRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1003,7 +1003,7 @@ VOID EnqueueChSwAnn( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1596,7 +1596,7 @@ static VOID PeerMeasureReportAction( if ((pMeasureReportInfo = kmalloc(sizeof(MEASURE_RPI_REPORT), GFP_ATOMIC)) == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __FUNCTION__, sizeof(MEASURE_RPI_REPORT))); + DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __func__, sizeof(MEASURE_RPI_REPORT))); return; } @@ -1705,7 +1705,7 @@ static VOID PeerTpcRepAction( { TpcReqDelete(pAd, pEntry->DialogToken); DBGPRINT(RT_DEBUG_TRACE, ("%s: DialogToken=%x, TxPwr=%d, LinkMargin=%d\n", - __FUNCTION__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); + __func__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); } } @@ -1821,7 +1821,7 @@ INT Set_MeasureReq_Proc( MeasureReqType = simple_strtol(thisChar, 0, 16); if (MeasureReqType > 3) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __FUNCTION__, MeasureReqType)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __func__, MeasureReqType)); return TRUE; } break; @@ -1833,10 +1833,10 @@ INT Set_MeasureReq_Proc( ArgIdx++; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __FUNCTION__, Aid, MeasureReqType, MeasureCh)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __func__, Aid, MeasureReqType, MeasureCh)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } @@ -1861,10 +1861,10 @@ INT Set_TpcReq_Proc( Aid = simple_strtol(arg, 0, 16); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __func__, Aid)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } diff --git a/drivers/staging/rt2860/rt_ate.c b/drivers/staging/rt2860/rt_ate.c index 2f07db565c30..f3316ec0b156 100644 --- a/drivers/staging/rt2860/rt_ate.c +++ b/drivers/staging/rt2860/rt_ate.c @@ -291,7 +291,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); } else// 5.5 GHz { @@ -318,7 +318,7 @@ static INT ATETxPwrHandler( R = (ULONG) TxPower; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __FUNCTION__, TxPower, R)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __func__, TxPower, R)); } if (pAd->ate.Channel <= 14) @@ -431,7 +431,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); if (pAd->ate.Channel <= 14) { @@ -2098,7 +2098,7 @@ INT Set_ATE_Load_E2P_Proc( UINT32 FileLength = 0; UINT32 value = simple_strtol(arg, 0, 10); - ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __FUNCTION__, value)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __func__, value)); if (value > 0) { @@ -2122,14 +2122,14 @@ INT Set_ATE_Load_E2P_Proc( if (IS_ERR(srcf)) { - ate_print("%s - Error %ld opening %s\n", __FUNCTION__, -PTR_ERR(srcf), src); + ate_print("%s - Error %ld opening %s\n", __func__, -PTR_ERR(srcf), src); break; } /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - ate_print("%s - %s does not have a read method\n", __FUNCTION__, src); + ate_print("%s - %s does not have a read method\n", __func__, src); break; } @@ -2142,7 +2142,7 @@ INT Set_ATE_Load_E2P_Proc( if (FileLength != EEPROM_SIZE) { ate_print("%s: error file length (=%d) in e2p.bin\n", - __FUNCTION__, FileLength); + __func__, FileLength); break; } else @@ -2174,7 +2174,7 @@ INT Set_ATE_Load_E2P_Proc( current->fsuid = orgfsuid; current->fsgid = orgfsgid; } - ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __FUNCTION__, ret)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __func__, ret)); return ret; @@ -2187,12 +2187,12 @@ INT Set_ATE_Load_E2P_Proc( USHORT WriteEEPROM[(EEPROM_SIZE/2)]; struct iwreq *wrq = (struct iwreq *)arg; - ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __FUNCTION__, wrq->u.data.length)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __func__, wrq->u.data.length)); if (wrq->u.data.length != EEPROM_SIZE) { ate_print("%s: error length (=%d) from host\n", - __FUNCTION__, wrq->u.data.length); + __func__, wrq->u.data.length); return FALSE; } else/* (wrq->u.data.length == EEPROM_SIZE) */ @@ -2211,7 +2211,7 @@ INT Set_ATE_Load_E2P_Proc( } while(FALSE); } - ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __FUNCTION__)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __func__)); return TRUE; @@ -3353,7 +3353,7 @@ static INT ATESetUpFrame( if (pPacket == NULL) { pAd->ate.TxCount = 0; - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s fail to alloc packet space.\n", __FUNCTION__)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s fail to alloc packet space.\n", __func__)); return -1; } pTxRing->Cell[TxIdx].pNextNdisPacket = pPacket; @@ -3646,7 +3646,7 @@ VOID RtmpDoAte( Command_Id = ntohs(pRaCfg->command_id); - ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __FUNCTION__, Command_Id)); + ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __func__, Command_Id)); switch (Command_Id) { @@ -5690,7 +5690,7 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.TxAntennaSel = 2; break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R1 */ @@ -5728,13 +5728,13 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.RxAntennaSel = 3; break; default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R3 */ default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } diff --git a/drivers/staging/rt2860/rt_linux.c b/drivers/staging/rt2860/rt_linux.c index 374c174c88ee..f14500931efb 100644 --- a/drivers/staging/rt2860/rt_linux.c +++ b/drivers/staging/rt2860/rt_linux.c @@ -406,7 +406,7 @@ NDIS_STATUS RTMPAllocateNdisPacket( skb_put(GET_OS_PKT_TYPE(pPacket), HeaderLen+DataLen); RTMP_SET_PACKET_SOURCE(pPacket, PKTSRC_NDIS); -// printk("%s : pPacket = %p, len = %d\n", __FUNCTION__, pPacket, GET_OS_PKT_LEN(pPacket)); +// printk("%s : pPacket = %p, len = %d\n", __func__, pPacket, GET_OS_PKT_LEN(pPacket)); *ppPacket = pPacket; return NDIS_STATUS_SUCCESS; } @@ -773,13 +773,13 @@ VOID RTMPSendWirelessEvent( if (event_table_len == 0) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __FUNCTION__, type)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __func__, type)); return; } if (event >= event_table_len) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __FUNCTION__, event)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __func__, event)); return; } @@ -817,14 +817,14 @@ VOID RTMPSendWirelessEvent( //send wireless event wireless_send_event(pAd->net_dev, IWEVCUSTOM, &wrqu, pBuf); - //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __FUNCTION__, pBuf)); + //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __func__, pBuf)); kfree(pBuf); } else - DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __func__)); #else - DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __func__)); #endif /* WIRELESS_EXT >= 15 */ } @@ -848,13 +848,13 @@ void send_monitor_packets( ASSERT(pRxBlk->pRxPacket); if (pRxBlk->DataSize < 10) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __FUNCTION__, pRxBlk->DataSize)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __func__, pRxBlk->DataSize)); goto err_free_sk_buff; } if (pRxBlk->DataSize + sizeof(wlan_ng_prism2_header) > RX_BUFFER_AGGRESIZE) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __FUNCTION__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __func__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); goto err_free_sk_buff; } @@ -910,7 +910,7 @@ void send_monitor_packets( if (skb_headroom(pOSPkt) < (sizeof(wlan_ng_prism2_header)+ header_len)) { if (pskb_expand_head(pOSPkt, (sizeof(wlan_ng_prism2_header) + header_len), 0, GFP_ATOMIC)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __func__)); goto err_free_sk_buff; } //end if } //end if diff --git a/drivers/staging/rt2860/rt_linux.h b/drivers/staging/rt2860/rt_linux.h index 0cc7cf23d762..0fd58f5109f2 100644 --- a/drivers/staging/rt2860/rt_linux.h +++ b/drivers/staging/rt2860/rt_linux.h @@ -131,7 +131,7 @@ typedef int (*HARD_START_XMIT_FUNC)(struct sk_buff *skb, struct net_device *net_ #define RT_MOD_INC_USE_COUNT() \ if (!try_module_get(THIS_MODULE)) \ { \ - DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __FUNCTION__)); \ + DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __func__)); \ return -1; \ } diff --git a/drivers/staging/rt2860/rt_profile.c b/drivers/staging/rt2860/rt_profile.c index cd7ffc8a6e8a..326a3cb52b9c 100644 --- a/drivers/staging/rt2860/rt_profile.c +++ b/drivers/staging/rt2860/rt_profile.c @@ -1024,7 +1024,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->MlmeAux.SsidLen = pAd->CommonCfg.SsidLen; NdisZeroMemory(pAd->MlmeAux.Ssid, NDIS_802_11_LENGTH_SSID); NdisMoveMemory(pAd->MlmeAux.Ssid, tmpbuf, pAd->MlmeAux.SsidLen); - DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __func__, tmpbuf)); } } } @@ -1043,7 +1043,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.BssType = BSS_INFRA; // Reset Ralink supplicant to not use, it will be set to start when UI set PMK key pAd->StaCfg.WpaState = SS_NOTUSE; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __FUNCTION__, pAd->StaCfg.BssType)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __func__, pAd->StaCfg.BssType)); } } #endif // CONFIG_STA_SUPPORT // @@ -1337,7 +1337,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.PortSecured = WPA_802_1X_PORT_NOT_SECURED; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1363,7 +1363,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.OrigWepStatus = pAd->StaCfg.WepStatus; pAd->StaCfg.bMixCipher = FALSE; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1400,7 +1400,7 @@ NDIS_STATUS RTMPReadParametersHook( else { err = 1; - DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __func__)); } if (err == 0) @@ -1416,7 +1416,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.WpaState = SS_NOTUSE; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __func__, tmpbuf)); } } } diff --git a/drivers/staging/rt2860/rtmp_def.h b/drivers/staging/rt2860/rtmp_def.h index bb6f37b0bdc2..be982147883f 100644 --- a/drivers/staging/rt2860/rtmp_def.h +++ b/drivers/staging/rt2860/rtmp_def.h @@ -333,7 +333,7 @@ /* sanity check for apidx */ #define MBSS_MR_APIDX_SANITY_CHECK(apidx) \ { if (apidx > MAX_MBSSID_NUM) { \ - printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __FUNCTION__, apidx); \ + printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __func__, apidx); \ apidx = MAIN_MBSSID; } } #define VALID_WCID(_wcid) ((_wcid) > 0 && (_wcid) < MAX_LEN_OF_MAC_TABLE ) diff --git a/drivers/staging/rt2860/sta_ioctl.c b/drivers/staging/rt2860/sta_ioctl.c index 9347d11b586e..3ea2b2c4ab0e 100644 --- a/drivers/staging/rt2860/sta_ioctl.c +++ b/drivers/staging/rt2860/sta_ioctl.c @@ -2200,7 +2200,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, } break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2219,7 +2219,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2228,7 +2228,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2257,7 +2257,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2290,7 +2290,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2321,7 +2321,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2341,7 +2341,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2370,12 +2370,12 @@ int rt_ioctl_siwauth(struct net_device *dev, { STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; case IW_AUTH_PRIVACY_INVOKED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2384,7 +2384,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2397,10 +2397,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2508,7 +2508,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2520,15 +2520,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2546,7 +2546,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, NdisMoveMemory(pAdapter->SharedKey[BSS0][keyIdx].Key, ext->key, ext->key_len); break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rt2870/2870_main_dev.c b/drivers/staging/rt2870/2870_main_dev.c index 91da4e2298ac..04c764d95d77 100644 --- a/drivers/staging/rt2870/2870_main_dev.c +++ b/drivers/staging/rt2870/2870_main_dev.c @@ -263,7 +263,7 @@ INT MlmeThread( * This is important in preemption kernels, which transfer the flow * of execution immediately upon a complete(). */ - DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__func__)); pObj->MLMEThr_pid = THREAD_PID_INIT_VALUE; @@ -465,7 +465,7 @@ INT TimerQThread( * This is important in preemption kernels, which transfer the flow * of execution immediately upon a complete(). */ - DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,( "<---%s\n",__func__)); pObj->TimerQThr_pid = THREAD_PID_INIT_VALUE; @@ -1258,7 +1258,7 @@ BOOLEAN RT28XXProbePostConfig( if (!(pAd->BulkInEpAddr && pAd->BulkOutEpAddr[0])) { - printk("%s: Could not find both bulk-in and bulk-out endpoints\n", __FUNCTION__); + printk("%s: Could not find both bulk-in and bulk-out endpoints\n", __func__); return FALSE; } diff --git a/drivers/staging/rt2870/common/2870_rtmp_init.c b/drivers/staging/rt2870/common/2870_rtmp_init.c index 5d89a310d1ea..9f5143b3922e 100644 --- a/drivers/staging/rt2870/common/2870_rtmp_init.c +++ b/drivers/staging/rt2870/common/2870_rtmp_init.c @@ -1087,7 +1087,7 @@ PNDIS_PACKET GetPacketFromRxRing( if (pRxWI->MPDUtotalByteCount > ThisFrameLen) { DBGPRINT(RT_DEBUG_ERROR, ("%s():pRxWIMPDUtotalByteCount(%d) large than RxDMALen(%ld)\n", - __FUNCTION__, pRxWI->MPDUtotalByteCount, ThisFrameLen)); + __func__, pRxWI->MPDUtotalByteCount, ThisFrameLen)); goto label_null; } #ifdef RT_BIG_ENDIAN @@ -1098,7 +1098,7 @@ PNDIS_PACKET GetPacketFromRxRing( pSkb = dev_alloc_skb(ThisFrameLen); if (pSkb == NULL) { - DBGPRINT(RT_DEBUG_ERROR,("%s():Cannot Allocate sk buffer for this Bulk-In buffer!\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR,("%s():Cannot Allocate sk buffer for this Bulk-In buffer!\n", __func__)); goto label_null; } diff --git a/drivers/staging/rt2870/common/ba_action.c b/drivers/staging/rt2870/common/ba_action.c index 95addb20bced..d9f738177e2b 100644 --- a/drivers/staging/rt2870/common/ba_action.c +++ b/drivers/staging/rt2870/common/ba_action.c @@ -595,7 +595,7 @@ VOID BAOriSessionAdd( pBAEntry->ORIBATimer.TimerValue = 0; //pFrame->TimeOutValue; - DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __FUNCTION__, pEntry->TXBAbitmap, + DBGPRINT(RT_DEBUG_TRACE,("%s : TXBAbitmap = %x, BAWinSize = %d, TimeOut = %ld\n", __func__, pEntry->TXBAbitmap, pBAEntry->BAWinSize, pBAEntry->ORIBATimer.TimerValue)); // SEND BAR ; @@ -669,7 +669,7 @@ BOOLEAN BARecSessionAdd( ba_refresh_reordering_mpdus(pAd, pBAEntry); } - DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __FUNCTION__, pAd->BATable.numAsRecipient, Idx, + DBGPRINT(RT_DEBUG_TRACE,("%s(%ld): Idx = %d, BAWinSize(req %d) = %d\n", __func__, pAd->BATable.numAsRecipient, Idx, pFrame->BaParm.BufSize, BAWinSize)); // Start fill in parameters. @@ -911,7 +911,7 @@ VOID BAOriSessionTearDown( return; } - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BAOriEntry[Idx]; DBGPRINT(RT_DEBUG_TRACE,("\t===>Idx = %ld, Wcid=%d.TID=%d, ORI_BA_Status = %d \n", Idx, Wcid, TID, pBAEntry->ORI_BA_Status)); @@ -970,7 +970,7 @@ VOID BARecSessionTearDown( if (Idx == 0) return; - DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE,("%s===>Wcid=%d.TID=%d \n", __func__, Wcid, TID)); pBAEntry = &pAd->BATable.BARecEntry[Idx]; @@ -1181,7 +1181,7 @@ VOID PeerAddBAReqAction( PULONG ptemp; PMAC_TABLE_ENTRY pMacEntry; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> (Wcid = %d)\n", __func__, Elem->Wcid)); //hex_dump("AddBAReq", Elem->Msg, Elem->MsgLen); @@ -1265,7 +1265,7 @@ VOID PeerAddBAReqAction( MiniportMMRequest(pAd, QID_AC_BE, pOutBuffer, FrameLen); MlmeFreeMemory(pAd, pOutBuffer); - DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __FUNCTION__, Elem->Wcid, ADDframe.BaParm.TID, + DBGPRINT(RT_DEBUG_TRACE, ("%s(%d): TID(%d), BufSize(%d) <== \n", __func__, Elem->Wcid, ADDframe.BaParm.TID, ADDframe.BaParm.BufSize)); } @@ -1284,7 +1284,7 @@ VOID PeerAddBARspAction( if (Elem->Wcid >= MAX_LEN_OF_MAC_TABLE) return; - DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __FUNCTION__, Elem->Wcid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s ==> Wcid(%d)\n", __func__, Elem->Wcid)); //hex_dump("PeerAddBARspAction()", Elem->Msg, Elem->MsgLen); @@ -1325,7 +1325,7 @@ VOID PeerDelBAAction( //PUCHAR pOutBuffer = NULL; PFRAME_DELBA_REQ pDelFrame = NULL; - DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE,("%s ==>\n", __func__)); //DELBA Request from unknown peer, ignore this. if (PeerDelBAActionSanity(pAd, Elem->Wcid, Elem->Msg, Elem->MsgLen)) { @@ -1362,7 +1362,7 @@ BOOLEAN CntlEnqueueForRecv( TID = (UCHAR)pFrame->BARControl.TID; - DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __FUNCTION__, Wcid, TID)); + DBGPRINT(RT_DEBUG_TRACE, ("%s(): BAR-Wcid(%ld), Tid (%d)\n", __func__, Wcid, TID)); //hex_dump("BAR", (PCHAR) pFrame, MsgLen); // Do nothing if the driver is starting halt state. // This might happen when timer already been fired before cancel timer with mlmehalt diff --git a/drivers/staging/rt2870/common/cmm_data.c b/drivers/staging/rt2870/common/cmm_data.c index 4477a8e64a29..fd809abf6df0 100644 --- a/drivers/staging/rt2870/common/cmm_data.c +++ b/drivers/staging/rt2870/common/cmm_data.c @@ -2009,7 +2009,7 @@ BOOLEAN MacTableDeleteEntry( } else { - printk("\n%s: Impossible Wcid = %d !!!!!\n", __FUNCTION__, wcid); + printk("\n%s: Impossible Wcid = %d !!!!!\n", __func__, wcid); } } diff --git a/drivers/staging/rt2870/common/cmm_info.c b/drivers/staging/rt2870/common/cmm_info.c index 40792e162374..47a1b1a73f09 100644 --- a/drivers/staging/rt2870/common/cmm_info.c +++ b/drivers/staging/rt2870/common/cmm_info.c @@ -2331,7 +2331,7 @@ VOID RTMPIoctlGetMacTable( wrq->u.data.length = sizeof(RT_802_11_MAC_TABLE); if (copy_to_user(wrq->u.data.pointer, &MacTab, wrq->u.data.length)) { - DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s: copy_to_user() fail\n", __func__)); } msg = (CHAR *) kmalloc(sizeof(CHAR)*(MAX_LEN_OF_MAC_TABLE*MAC_LINE_LEN), MEM_ALLOC_FLAG); diff --git a/drivers/staging/rt2870/common/dfs.c b/drivers/staging/rt2870/common/dfs.c index 23cf1510e2d2..b09bba5cb206 100644 --- a/drivers/staging/rt2870/common/dfs.c +++ b/drivers/staging/rt2870/common/dfs.c @@ -428,7 +428,7 @@ INT Set_ChMovingTime_Proc( pAd->CommonCfg.RadarDetect.ChMovingTime = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.ChMovingTime)); return TRUE; @@ -444,7 +444,7 @@ INT Set_LongPulseRadarTh_Proc( pAd->CommonCfg.RadarDetect.LongPulseRadarTh = Value; - DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __FUNCTION__, + DBGPRINT(RT_DEBUG_TRACE, ("%s:: %d\n", __func__, pAd->CommonCfg.RadarDetect.LongPulseRadarTh)); return TRUE; diff --git a/drivers/staging/rt2870/common/rtmp_init.c b/drivers/staging/rt2870/common/rtmp_init.c index 87028fd60bc2..870a00da3dac 100644 --- a/drivers/staging/rt2870/common/rtmp_init.c +++ b/drivers/staging/rt2870/common/rtmp_init.c @@ -2746,7 +2746,7 @@ NDIS_STATUS NICLoadFirmware( #ifdef BIN_IN_FILE #define NICLF_DEFAULT_USE() \ flg_default_firm_use = TRUE; \ - printk("%s - Use default firmware!\n", __FUNCTION__); + printk("%s - Use default firmware!\n", __func__); NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PUCHAR src; @@ -2761,7 +2761,7 @@ NDIS_STATUS NICLoadFirmware( BOOLEAN flg_default_firm_use = FALSE; - DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("===> %s\n", __func__)); /* init */ pFirmwareImage = NULL; @@ -2784,7 +2784,7 @@ NDIS_STATUS NICLoadFirmware( if (pFirmwareImage == NULL) { /* allocate fail, use default firmware array in firmware.h */ - printk("%s - Allocate memory fail!\n", __FUNCTION__); + printk("%s - Allocate memory fail!\n", __func__); NICLF_DEFAULT_USE(); } else @@ -2805,7 +2805,7 @@ NDIS_STATUS NICLoadFirmware( if (IS_ERR(srcf)) { printk("%s - Error %ld opening %s\n", - __FUNCTION__, -PTR_ERR(srcf), src); + __func__, -PTR_ERR(srcf), src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2813,7 +2813,7 @@ NDIS_STATUS NICLoadFirmware( /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - printk("%s - %s does not have a write method\n", __FUNCTION__, src); + printk("%s - %s does not have a write method\n", __func__, src); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -2827,7 +2827,7 @@ NDIS_STATUS NICLoadFirmware( if (FileLength != MAX_FIRMWARE_IMAGE_SIZE) { printk("%s: error file length (=%d) in RT2860AP.BIN\n", - __FUNCTION__, FileLength); + __func__, FileLength); NICLF_DEFAULT_USE(); break; } @@ -2850,7 +2850,7 @@ NDIS_STATUS NICLoadFirmware( /* CRC fail */ printk("%s: CRC = 0x%02x 0x%02x " "error, should be 0x%02x 0x%02x\n", - __FUNCTION__, + __func__, pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-2], pFirmwareImage[MAX_FIRMWARE_IMAGE_SIZE-1], (UCHAR)(crc>>8), (UCHAR)(crc)); @@ -2869,7 +2869,7 @@ NDIS_STATUS NICLoadFirmware( ((FIRMWARE_MAJOR_VERSION << 8) + FIRMWARE_MINOR_VERSION)) { - printk("%s: firmware version too old!\n", __FUNCTION__); + printk("%s: firmware version too old!\n", __func__); NICLF_DEFAULT_USE(); break; } /* End of if */ @@ -3024,10 +3024,10 @@ NDIS_STATUS NICLoadFirmware( #if 0 DBGPRINT(RT_DEBUG_TRACE, - ("<=== %s (src=%s, status=%d)\n", __FUNCTION__, src, Status)); + ("<=== %s (src=%s, status=%d)\n", __func__, src, Status)); #else DBGPRINT(RT_DEBUG_TRACE, - ("<=== %s (status=%d)\n", __FUNCTION__, Status)); + ("<=== %s (status=%d)\n", __func__, Status)); #endif return Status; } /* End of NICLoadFirmware */ diff --git a/drivers/staging/rt2870/common/rtusb_bulk.c b/drivers/staging/rt2870/common/rtusb_bulk.c index 3c6ba1b9deb1..c46d9166ccf4 100644 --- a/drivers/staging/rt2870/common/rtusb_bulk.c +++ b/drivers/staging/rt2870/common/rtusb_bulk.c @@ -329,7 +329,7 @@ VOID RTUSBBulkOutDataPacket( if (pTxInfo->QSEL != FIFO_EDCA) { - printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __FUNCTION__, pTxInfo->QSEL); + printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __func__, pTxInfo->QSEL); printk("\tCWPos=%ld, NBPos=%ld, ENBPos=%ld, bCopy=%d!\n", pHTTXContext->CurWritePosition, pHTTXContext->NextBulkOutPosition, pHTTXContext->ENextBulkOutPosition, pHTTXContext->bCopySavePad); hex_dump("Wrong QSel Pkt:", (PUCHAR)&pWirelessPkt[TmpBulkEndPos], (pHTTXContext->CurWritePosition - pHTTXContext->NextBulkOutPosition)); } @@ -942,7 +942,7 @@ VOID RTUSBBulkOutMLMEPacket( pTxInfo = (PTXINFO_STRUC)pMLMEContext->TransferBuffer; if (pTxInfo->QSEL != FIFO_EDCA) { - printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __FUNCTION__, pTxInfo->QSEL); + printk("%s(): ====> pTxInfo->QueueSel(%d)!= FIFO_EDCA!!!!\n", __func__, pTxInfo->QSEL); printk("\tMLME_Index=%d!\n", Index); hex_dump("Wrong QSel Pkt:", (PUCHAR)pMLMEContext->TransferBuffer, pTxInfo->USBDMATxPktLen); } diff --git a/drivers/staging/rt2870/common/spectrum.c b/drivers/staging/rt2870/common/spectrum.c index abba8405184f..43782ce9288f 100644 --- a/drivers/staging/rt2870/common/spectrum.c +++ b/drivers/staging/rt2870/common/spectrum.c @@ -48,7 +48,7 @@ VOID MeasureReqTabInit( if (pAd->CommonCfg.pMeasureReqTab) NdisZeroMemory(pAd->CommonCfg.pMeasureReqTab, sizeof(MEASURE_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pMeasureReqTab.\n", __func__)); return; } @@ -76,7 +76,7 @@ static PMEASURE_REQ_ENTRY MeasureReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -113,7 +113,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return NULL; } @@ -174,7 +174,7 @@ static PMEASURE_REQ_ENTRY MeasureReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -209,7 +209,7 @@ static VOID MeasureReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pMeasureReqTab doesn't exist.\n", __func__)); return; } @@ -266,7 +266,7 @@ VOID TpcReqTabInit( if (pAd->CommonCfg.pTpcReqTab) NdisZeroMemory(pAd->CommonCfg.pTpcReqTab, sizeof(TPC_REQ_TAB)); else - DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s Fail to alloc memory for pAd->CommonCfg.pTpcReqTab.\n", __func__)); return; } @@ -294,7 +294,7 @@ static PTPC_REQ_ENTRY TpcReqLookUp( if (pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -332,7 +332,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return NULL; } @@ -393,7 +393,7 @@ static PTPC_REQ_ENTRY TpcReqInsert( else { pEntry = NULL; - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab tab full.\n", __func__)); } // add this Neighbor entry into HASH table @@ -428,7 +428,7 @@ static VOID TpcReqDelete( if(pTab == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: pTpcReqTab doesn't exist.\n", __func__)); return; } @@ -781,7 +781,7 @@ VOID EnqueueMeasurementReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -843,7 +843,7 @@ VOID EnqueueMeasurementRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -897,7 +897,7 @@ VOID EnqueueTPCReq( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -949,7 +949,7 @@ VOID EnqueueTPCRep( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1002,7 +1002,7 @@ VOID EnqueueChSwAnn( NStatus = MlmeAllocateMemory(pAd, (PVOID)&pOutBuffer); //Get an unused nonpaged memory if(NStatus != NDIS_STATUS_SUCCESS) { - DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s() allocate memory failed \n", __func__)); return; } NdisMoveMemory(pOutBuffer, (PCHAR)&ActHdr, sizeof(HEADER_802_11)); @@ -1595,7 +1595,7 @@ static VOID PeerMeasureReportAction( if ((pMeasureReportInfo = kmalloc(sizeof(MEASURE_RPI_REPORT), GFP_ATOMIC)) == NULL) { - DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __FUNCTION__, sizeof(MEASURE_RPI_REPORT))); + DBGPRINT(RT_DEBUG_ERROR, ("%s unable to alloc memory for measure report buffer (size=%d).\n", __func__, sizeof(MEASURE_RPI_REPORT))); return; } @@ -1704,7 +1704,7 @@ static VOID PeerTpcRepAction( { TpcReqDelete(pAd, pEntry->DialogToken); DBGPRINT(RT_DEBUG_TRACE, ("%s: DialogToken=%x, TxPwr=%d, LinkMargin=%d\n", - __FUNCTION__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); + __func__, DialogToken, TpcRepInfo.TxPwr, TpcRepInfo.LinkMargin)); } } @@ -1820,7 +1820,7 @@ INT Set_MeasureReq_Proc( MeasureReqType = simple_strtol(thisChar, 0, 16); if (MeasureReqType > 3) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __FUNCTION__, MeasureReqType)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow MeasureReqType(%d)\n", __func__, MeasureReqType)); return TRUE; } break; @@ -1832,10 +1832,10 @@ INT Set_MeasureReq_Proc( ArgIdx++; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __FUNCTION__, Aid, MeasureReqType, MeasureCh)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d, MeasureReqType=%d MeasureCh=%d\n", __func__, Aid, MeasureReqType, MeasureCh)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } @@ -1860,10 +1860,10 @@ INT Set_TpcReq_Proc( Aid = simple_strtol(arg, 0, 16); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Aid = %d\n", __func__, Aid)); if (!VALID_WCID(Aid)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __FUNCTION__, Aid)); + DBGPRINT(RT_DEBUG_ERROR, ("%s: unknow sta of Aid(%d)\n", __func__, Aid)); return TRUE; } diff --git a/drivers/staging/rt2870/rt_ate.c b/drivers/staging/rt2870/rt_ate.c index e99b3da0b62d..27c763ee9276 100644 --- a/drivers/staging/rt2870/rt_ate.c +++ b/drivers/staging/rt2870/rt_ate.c @@ -314,7 +314,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); } else// 5.5 GHz { @@ -341,7 +341,7 @@ static INT ATETxPwrHandler( R = (ULONG) TxPower; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __FUNCTION__, TxPower, R)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R=%lu)\n", __func__, TxPower, R)); } if (pAd->ate.Channel <= 14) @@ -454,7 +454,7 @@ static INT ATETxPwrHandler( Bbp94 = BBPR94_DEFAULT; } - ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __FUNCTION__, TxPower, R, Bbp94)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("%s (TxPower=%d, R3=%ld, BBP_R94=%d)\n", __func__, TxPower, R, Bbp94)); if (pAd->ate.Channel <= 14) { @@ -2261,7 +2261,7 @@ INT Set_ATE_Load_E2P_Proc( UINT32 FileLength = 0; UINT32 value = simple_strtol(arg, 0, 10); - ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __FUNCTION__, value)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("===> %s (value=%d)\n\n", __func__, value)); if (value > 0) { @@ -2285,14 +2285,14 @@ INT Set_ATE_Load_E2P_Proc( if (IS_ERR(srcf)) { - ate_print("%s - Error %ld opening %s\n", __FUNCTION__, -PTR_ERR(srcf), src); + ate_print("%s - Error %ld opening %s\n", __func__, -PTR_ERR(srcf), src); break; } /* the object must have a read method */ if ((srcf->f_op == NULL) || (srcf->f_op->read == NULL)) { - ate_print("%s - %s does not have a read method\n", __FUNCTION__, src); + ate_print("%s - %s does not have a read method\n", __func__, src); break; } @@ -2305,7 +2305,7 @@ INT Set_ATE_Load_E2P_Proc( if (FileLength != EEPROM_SIZE) { ate_print("%s: error file length (=%d) in e2p.bin\n", - __FUNCTION__, FileLength); + __func__, FileLength); break; } else @@ -2337,7 +2337,7 @@ INT Set_ATE_Load_E2P_Proc( current->fsuid = orgfsuid; current->fsgid = orgfsgid; } - ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __FUNCTION__, ret)); + ATEDBGPRINT(RT_DEBUG_ERROR, ("<=== %s (ret=%d)\n", __func__, ret)); return ret; @@ -2350,12 +2350,12 @@ INT Set_ATE_Load_E2P_Proc( USHORT WriteEEPROM[(EEPROM_SIZE/2)]; struct iwreq *wrq = (struct iwreq *)arg; - ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __FUNCTION__, wrq->u.data.length)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("===> %s (wrq->u.data.length = %d)\n\n", __func__, wrq->u.data.length)); if (wrq->u.data.length != EEPROM_SIZE) { ate_print("%s: error length (=%d) from host\n", - __FUNCTION__, wrq->u.data.length); + __func__, wrq->u.data.length); return FALSE; } else/* (wrq->u.data.length == EEPROM_SIZE) */ @@ -2374,7 +2374,7 @@ INT Set_ATE_Load_E2P_Proc( } while(FALSE); } - ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __FUNCTION__)); + ATEDBGPRINT(RT_DEBUG_TRACE, ("<=== %s\n", __func__)); return TRUE; @@ -4083,7 +4083,7 @@ VOID RtmpDoAte( Command_Id = ntohs(pRaCfg->command_id); - ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __FUNCTION__, Command_Id)); + ATEDBGPRINT(RT_DEBUG_TRACE,("\n%s: Command_Id = 0x%04x !\n", __func__, Command_Id)); switch (Command_Id) { @@ -6117,7 +6117,7 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.TxAntennaSel = 2; break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R1 */ @@ -6155,13 +6155,13 @@ BOOLEAN SyncTxRxConfig(PRTMP_ADAPTER pAd, USHORT offset, UCHAR value) pAd->ate.RxAntennaSel = 3; break; default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Impossible! : return FALSE; \n", __func__)); return FALSE; } break;/* case BBP_R3 */ default: - DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s -- Sth. wrong! : return FALSE; \n", __func__)); return FALSE; } diff --git a/drivers/staging/rt2870/rt_linux.c b/drivers/staging/rt2870/rt_linux.c index f2ea8ebcd042..992e3d16f9b4 100644 --- a/drivers/staging/rt2870/rt_linux.c +++ b/drivers/staging/rt2870/rt_linux.c @@ -404,7 +404,7 @@ NDIS_STATUS RTMPAllocateNdisPacket( skb_put(GET_OS_PKT_TYPE(pPacket), HeaderLen+DataLen); RTMP_SET_PACKET_SOURCE(pPacket, PKTSRC_NDIS); -// printk("%s : pPacket = %p, len = %d\n", __FUNCTION__, pPacket, GET_OS_PKT_LEN(pPacket)); +// printk("%s : pPacket = %p, len = %d\n", __func__, pPacket, GET_OS_PKT_LEN(pPacket)); *ppPacket = pPacket; return NDIS_STATUS_SUCCESS; } @@ -814,13 +814,13 @@ VOID RTMPSendWirelessEvent( if (event_table_len == 0) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __FUNCTION__, type)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The type(%0x02x) is not valid.\n", __func__, type)); return; } if (event >= event_table_len) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __FUNCTION__, event)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The event(%0x02x) is not valid.\n", __func__, event)); return; } @@ -858,14 +858,14 @@ VOID RTMPSendWirelessEvent( //send wireless event wireless_send_event(pAd->net_dev, IWEVCUSTOM, &wrqu, pBuf); - //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __FUNCTION__, pBuf)); + //DBGPRINT(RT_DEBUG_TRACE, ("%s : %s\n", __func__, pBuf)); kfree(pBuf); } else - DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Can't allocate memory for wireless event.\n", __func__)); #else - DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : The Wireless Extension MUST be v15 or newer.\n", __func__)); #endif /* WIRELESS_EXT >= 15 */ } @@ -889,13 +889,13 @@ void send_monitor_packets( ASSERT(pRxBlk->pRxPacket); if (pRxBlk->DataSize < 10) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __FUNCTION__, pRxBlk->DataSize)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too small! (%d)\n", __func__, pRxBlk->DataSize)); goto err_free_sk_buff; } if (pRxBlk->DataSize + sizeof(wlan_ng_prism2_header) > RX_BUFFER_AGGRESIZE) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __FUNCTION__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Size is too large! (%d)\n", __func__, pRxBlk->DataSize + sizeof(wlan_ng_prism2_header))); goto err_free_sk_buff; } @@ -951,7 +951,7 @@ void send_monitor_packets( if (skb_headroom(pOSPkt) < (sizeof(wlan_ng_prism2_header)+ header_len)) { if (pskb_expand_head(pOSPkt, (sizeof(wlan_ng_prism2_header) + header_len), 0, GFP_ATOMIC)) { - DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s : Reallocate header size of sk_buff fail!\n", __func__)); goto err_free_sk_buff; } //end if } //end if diff --git a/drivers/staging/rt2870/rt_linux.h b/drivers/staging/rt2870/rt_linux.h index 814297577261..859f9cef0a19 100644 --- a/drivers/staging/rt2870/rt_linux.h +++ b/drivers/staging/rt2870/rt_linux.h @@ -124,7 +124,7 @@ typedef int (*HARD_START_XMIT_FUNC)(struct sk_buff *skb, struct net_device *net_ #define RT_MOD_INC_USE_COUNT() \ if (!try_module_get(THIS_MODULE)) \ { \ - DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __FUNCTION__)); \ + DBGPRINT(RT_DEBUG_ERROR, ("%s: cannot reserve module\n", __func__)); \ return -1; \ } diff --git a/drivers/staging/rt2870/rt_main_dev.c b/drivers/staging/rt2870/rt_main_dev.c index b990f8a61641..313ecea0bfa8 100644 --- a/drivers/staging/rt2870/rt_main_dev.c +++ b/drivers/staging/rt2870/rt_main_dev.c @@ -1554,7 +1554,7 @@ int rt28xx_packet_xmit(struct sk_buff *skb) #if 0 // if ((pkt->data[0] & 0x1) == 0) { - //hex_dump(__FUNCTION__, pkt->data, pkt->len); + //hex_dump(__func__, pkt->data, pkt->len); printk("pPacket = %x\n", pPacket); } #endif diff --git a/drivers/staging/rt2870/rt_profile.c b/drivers/staging/rt2870/rt_profile.c index c4474a6428ac..467fea35e4a9 100644 --- a/drivers/staging/rt2870/rt_profile.c +++ b/drivers/staging/rt2870/rt_profile.c @@ -1024,7 +1024,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->MlmeAux.SsidLen = pAd->CommonCfg.SsidLen; NdisZeroMemory(pAd->MlmeAux.Ssid, NDIS_802_11_LENGTH_SSID); NdisMoveMemory(pAd->MlmeAux.Ssid, tmpbuf, pAd->MlmeAux.SsidLen); - DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(SSID=%s)\n", __func__, tmpbuf)); } } } @@ -1043,7 +1043,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.BssType = BSS_INFRA; // Reset Ralink supplicant to not use, it will be set to start when UI set PMK key pAd->StaCfg.WpaState = SS_NOTUSE; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __FUNCTION__, pAd->StaCfg.BssType)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(NetworkType=%d)\n", __func__, pAd->StaCfg.BssType)); } } #endif // CONFIG_STA_SUPPORT // @@ -1341,7 +1341,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.PortSecured = WPA_802_1X_PORT_NOT_SECURED; - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1368,7 +1368,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.bMixCipher = FALSE; //RTMPMakeRSNIE(pAd, pAd->StaCfg.AuthMode, pAd->StaCfg.WepStatus, 0); - DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __FUNCTION__, pAd->StaCfg.WepStatus)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(EncrypType=%d)\n", __func__, pAd->StaCfg.WepStatus)); } #endif // CONFIG_STA_SUPPORT // } @@ -1405,7 +1405,7 @@ NDIS_STATUS RTMPReadParametersHook( else { err = 1; - DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_ERROR, ("%s::(WPAPSK key-string required 8 ~ 64 characters!)\n", __func__)); } if (err == 0) @@ -1436,7 +1436,7 @@ NDIS_STATUS RTMPReadParametersHook( pAd->StaCfg.WpaState = SS_NOTUSE; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __FUNCTION__, tmpbuf)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::(WPAPSK=%s)\n", __func__, tmpbuf)); } } } diff --git a/drivers/staging/rt2870/rtmp_def.h b/drivers/staging/rt2870/rtmp_def.h index 9b86325b4c55..c0f7561a9d9d 100644 --- a/drivers/staging/rt2870/rtmp_def.h +++ b/drivers/staging/rt2870/rtmp_def.h @@ -342,7 +342,7 @@ /* sanity check for apidx */ #define MBSS_MR_APIDX_SANITY_CHECK(apidx) \ { if (apidx > MAX_MBSSID_NUM) { \ - printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __FUNCTION__, apidx); \ + printk("%s> Error! apidx = %d > MAX_MBSSID_NUM!\n", __func__, apidx); \ apidx = MAIN_MBSSID; } } #define VALID_WCID(_wcid) ((_wcid) > 0 && (_wcid) < MAX_LEN_OF_MAC_TABLE ) diff --git a/drivers/staging/rt2870/sta_ioctl.c b/drivers/staging/rt2870/sta_ioctl.c index 422f39f9004a..91f0fab11313 100644 --- a/drivers/staging/rt2870/sta_ioctl.c +++ b/drivers/staging/rt2870/sta_ioctl.c @@ -2224,7 +2224,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, wrq->length = strlen(extra) + 1; // 1: size of '\0' break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2252,7 +2252,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2267,7 +2267,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2281,7 +2281,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2314,7 +2314,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2345,7 +2345,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2365,7 +2365,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2395,7 +2395,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; @@ -2408,7 +2408,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.PairCipher = Ndis802_11WEPDisabled; pAdapter->StaCfg.GroupCipher = Ndis802_11WEPDisabled; }*/ - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2418,7 +2418,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2431,10 +2431,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2542,7 +2542,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2554,15 +2554,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2595,7 +2595,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, } break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rt2870/tmp60 b/drivers/staging/rt2870/tmp60 index 992096a248c5..975e44484379 100644 --- a/drivers/staging/rt2870/tmp60 +++ b/drivers/staging/rt2870/tmp60 @@ -2224,7 +2224,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, wrq->length = strlen(extra) + 1; // 1: size of '\0' break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2252,7 +2252,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2267,7 +2267,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2281,7 +2281,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2314,7 +2314,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2345,7 +2345,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2365,7 +2365,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2395,7 +2395,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; @@ -2408,7 +2408,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.PairCipher = Ndis802_11WEPDisabled; pAdapter->StaCfg.GroupCipher = Ndis802_11WEPDisabled; }*/ - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2418,7 +2418,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2431,10 +2431,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2542,7 +2542,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2554,15 +2554,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2580,7 +2580,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, NdisMoveMemory(pAdapter->SharedKey[BSS0][keyIdx].Key, ext->key, ext->key_len); break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rt2870/tmp61 b/drivers/staging/rt2870/tmp61 index 992096a248c5..975e44484379 100644 --- a/drivers/staging/rt2870/tmp61 +++ b/drivers/staging/rt2870/tmp61 @@ -2224,7 +2224,7 @@ rt_private_show(struct net_device *dev, struct iw_request_info *info, wrq->length = strlen(extra) + 1; // 1: size of '\0' break; default: - DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __FUNCTION__, subcmd)); + DBGPRINT(RT_DEBUG_TRACE, ("%s - unknow subcmd = %d\n", __func__, subcmd)); break; } @@ -2243,7 +2243,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, MLME_DISASSOC_REQ_STRUCT DisAssocReq; MLME_DEAUTH_REQ_STRUCT DeAuthReq; - DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s\n", __func__)); if (pMlme == NULL) return -EINVAL; @@ -2252,7 +2252,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, { #ifdef IW_MLME_DEAUTH case IW_MLME_DEAUTH: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DEAUTH\n", __func__)); COPY_MAC_ADDR(DeAuthReq.Addr, pAd->CommonCfg.Bssid); DeAuthReq.Reason = pMlme->reason_code; MsgElem.MsgLen = sizeof(MLME_DEAUTH_REQ_STRUCT); @@ -2267,7 +2267,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, #endif // IW_MLME_DEAUTH // #ifdef IW_MLME_DISASSOC case IW_MLME_DISASSOC: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - IW_MLME_DISASSOC\n", __func__)); COPY_MAC_ADDR(DisAssocReq.Addr, pAd->CommonCfg.Bssid); DisAssocReq.Reason = pMlme->reason_code; @@ -2281,7 +2281,7 @@ int rt_ioctl_siwmlme(struct net_device *dev, break; #endif // IW_MLME_DISASSOC // default: - DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("====> %s - Unknow Command\n", __func__)); break; } @@ -2314,7 +2314,7 @@ int rt_ioctl_siwauth(struct net_device *dev, else if (param->value == IW_AUTH_WPA_VERSION_WPA2) pAdapter->StaCfg.AuthMode = Ndis802_11AuthModeWPA2PSK; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_PAIRWISE: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2345,7 +2345,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.OrigWepStatus = pAdapter->StaCfg.WepStatus; pAdapter->StaCfg.PairCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_PAIRWISE - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_CIPHER_GROUP: if (param->value == IW_AUTH_CIPHER_NONE) @@ -2365,7 +2365,7 @@ int rt_ioctl_siwauth(struct net_device *dev, { pAdapter->StaCfg.GroupCipher = Ndis802_11Encryption3Enabled; } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_CIPHER_GROUP - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_KEY_MGMT: if (param->value == IW_AUTH_KEY_MGMT_802_1X) @@ -2395,7 +2395,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_KEY_MGMT - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: break; @@ -2408,7 +2408,7 @@ int rt_ioctl_siwauth(struct net_device *dev, pAdapter->StaCfg.PairCipher = Ndis802_11WEPDisabled; pAdapter->StaCfg.GroupCipher = Ndis802_11WEPDisabled; }*/ - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_PRIVACY_INVOKED - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_DROP_UNENCRYPTED: if (param->value != 0) @@ -2418,7 +2418,7 @@ int rt_ioctl_siwauth(struct net_device *dev, //pAdapter->StaCfg.PortSecured = WPA_802_1X_PORT_SECURED; STA_PORT_SECURED(pAdapter); } - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_VERSION - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_80211_AUTH_ALG: if (param->value & IW_AUTH_ALG_SHARED_KEY) @@ -2431,10 +2431,10 @@ int rt_ioctl_siwauth(struct net_device *dev, } else return -EINVAL; - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_80211_AUTH_ALG - param->value = %d!\n", __func__, param->value)); break; case IW_AUTH_WPA_ENABLED: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __FUNCTION__, param->value)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_AUTH_WPA_ENABLED - Driver supports WPA!(param->value = %d)\n", __func__, param->value)); break; default: return -EOPNOTSUPP; @@ -2542,7 +2542,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, pAdapter->SharedKey[BSS0][keyIdx].CipherAlg = CIPHER_NONE; AsicRemoveSharedKeyEntry(pAdapter, 0, (UCHAR)keyIdx); NdisZeroMemory(&pAdapter->SharedKey[BSS0][keyIdx], sizeof(CIPHER_KEY)); - DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __FUNCTION__, encoding->flags)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::Remove all keys!(encoding->flags = %x)\n", __func__, encoding->flags)); } else { @@ -2554,15 +2554,15 @@ int rt_ioctl_siwencodeext(struct net_device *dev, if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) { pAdapter->StaCfg.DefaultKeyId = keyIdx; - DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __FUNCTION__, pAdapter->StaCfg.DefaultKeyId)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::DefaultKeyId = %d\n", __func__, pAdapter->StaCfg.DefaultKeyId)); } switch (alg) { case IW_ENCODE_ALG_NONE: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __FUNCTION__)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_NONE\n", __func__)); break; case IW_ENCODE_ALG_WEP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __FUNCTION__, ext->key_len, keyIdx)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_WEP - ext->key_len = %d, keyIdx = %d\n", __func__, ext->key_len, keyIdx)); if (ext->key_len == MAX_WEP_KEY_SIZE) { pAdapter->SharedKey[BSS0][keyIdx].KeyLen = MAX_WEP_KEY_SIZE; @@ -2580,7 +2580,7 @@ int rt_ioctl_siwencodeext(struct net_device *dev, NdisMoveMemory(pAdapter->SharedKey[BSS0][keyIdx].Key, ext->key, ext->key_len); break; case IW_ENCODE_ALG_TKIP: - DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __FUNCTION__, keyIdx, ext->key_len)); + DBGPRINT(RT_DEBUG_TRACE, ("%s::IW_ENCODE_ALG_TKIP - keyIdx = %d, ext->key_len = %d\n", __func__, keyIdx, ext->key_len)); if (ext->key_len == 32) { if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) diff --git a/drivers/staging/rtl8187se/ieee80211.h b/drivers/staging/rtl8187se/ieee80211.h index bf06abeeaeb8..58336080ad50 100644 --- a/drivers/staging/rtl8187se/ieee80211.h +++ b/drivers/staging/rtl8187se/ieee80211.h @@ -396,7 +396,7 @@ extern u32 ieee80211_debug_level; #define IEEE80211_DEBUG(level, fmt, args...) \ do { if (ieee80211_debug_level & (level)) \ printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_IEEE80211_DEBUG */ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211.h b/drivers/staging/rtl8187se/ieee80211/ieee80211.h index bf06abeeaeb8..58336080ad50 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211.h +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211.h @@ -396,7 +396,7 @@ extern u32 ieee80211_debug_level; #define IEEE80211_DEBUG(level, fmt, args...) \ do { if (ieee80211_debug_level & (level)) \ printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ - in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) + in_interrupt() ? 'I' : 'U', __func__ , ## args); } while (0) #else #define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) #endif /* CONFIG_IEEE80211_DEBUG */ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c index 7d890c328b0e..08add385790f 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_ccmp.c @@ -490,7 +490,7 @@ static char * ieee80211_ccmp_print_stats(char *p, void *priv) void ieee80211_ccmp_null(void) { -// printk("============>%s()\n", __FUNCTION__); +// printk("============>%s()\n", __func__); return; } static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = { diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c index e560b1e11020..de97bbec0da5 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_tkip.c @@ -984,7 +984,7 @@ void ieee80211_crypto_tkip_exit(void) void ieee80211_tkip_null(void) { -// printk("============>%s()\n", __FUNCTION__); +// printk("============>%s()\n", __func__); return; } diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c index f973daeeeb0f..68a22b32fcf0 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_crypt_wep.c @@ -380,7 +380,7 @@ void ieee80211_crypto_wep_exit(void) void ieee80211_wep_null(void) { -// printk("============>%s()\n", __FUNCTION__); +// printk("============>%s()\n", __func__); return; } #if 0 diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c index 79ec64959cf6..23519b37538f 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_rx.c @@ -1635,7 +1635,7 @@ inline void update_network(struct ieee80211_network *dst, memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); dst->rates_ex_len = src->rates_ex_len; dst->HighestOperaRate= src->HighestOperaRate; - //printk("==========>in %s: src->ssid is %s,chan is %d\n",__FUNCTION__,src->ssid,src->channel); + //printk("==========>in %s: src->ssid is %s,chan is %d\n",__func__,src->ssid,src->channel); //YJ,add,080819,for hidden ap if(src->ssid_len > 0) diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c index fcffee516d51..e5752f615e09 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c @@ -731,7 +731,7 @@ void ieee80211_softmac_scan_wq(struct ieee80211_device *ieee) memcpy(channel_map, GET_DOT11D_INFO(ieee)->channel_map, MAX_CHANNEL_NUMBER+1); #endif // printk("ieee80211_softmac_scan_wq ENABLE_IPS\n"); -// printk("in %s\n",__FUNCTION__); +// printk("in %s\n",__func__); down(&ieee->scan_sem); do{ @@ -1785,7 +1785,7 @@ void ieee80211_associate_step1(struct ieee80211_device *ieee) //If call dev_kfree_skb_any,a warning will ocur.... //KERNEL: assertion (!atomic_read(&skb->users)) failed at net/core/dev.c (1708) //So ... 1204 by lawrence. - //printk("\nIn %s,line %d call kfree skb.",__FUNCTION__,__LINE__); + //printk("\nIn %s,line %d call kfree skb.",__func__,__LINE__); //dev_kfree_skb_any(skb);//edit by thomas } } @@ -2432,7 +2432,7 @@ inline void ieee80211_sta_ps(struct ieee80211_device *ieee) } sleep = ieee80211_sta_ps_sleep(ieee,&th, &tl); -// printk("===>%s,%d[2 wake, 1 sleep, 0 do nothing], ieee->sta_sleep = %d\n",__FUNCTION__, sleep,ieee->sta_sleep); +// printk("===>%s,%d[2 wake, 1 sleep, 0 do nothing], ieee->sta_sleep = %d\n",__func__, sleep,ieee->sta_sleep); /* 2 wake, 1 sleep, 0 do nothing */ if(sleep == 0) goto out; @@ -2510,7 +2510,7 @@ void ieee80211_ps_tx_ack(struct ieee80211_device *ieee, short success) /* Null frame with PS bit set */ if(success){ - // printk("==================> %s::enter sleep state\n",__FUNCTION__); + // printk("==================> %s::enter sleep state\n",__func__); ieee->sta_sleep = 1; ieee->enter_sleep_state(ieee->dev,ieee->ps_th,ieee->ps_tl); } diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c index 43b8aecee9de..93af37e2d31a 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c @@ -31,7 +31,7 @@ int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info { int ret; struct iw_freq *fwrq = & wrqu->freq; -// printk("in %s\n",__FUNCTION__); +// printk("in %s\n",__func__); down(&ieee->wx_sem); if(ieee->iw_mode == IW_MODE_INFRA){ diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c index c7d9f4fda413..6aad61e78041 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_wx.c @@ -663,7 +663,7 @@ int ieee80211_wx_set_encode_ext(struct ieee80211_device *ieee, ret = -EINVAL; goto done; } -// printk("8-09-08-9=====>%s, alg name:%s\n",__FUNCTION__, alg); +// printk("8-09-08-9=====>%s, alg name:%s\n",__func__, alg); ops = ieee80211_get_crypto_ops(alg); if (ops == NULL) { @@ -757,7 +757,7 @@ int ieee80211_wx_set_mlme(struct ieee80211_device *ieee, union iwreq_data *wrqu, char *extra) { struct iw_mlme *mlme = (struct iw_mlme *) extra; -// printk("\ndkgadfslkdjgalskdf===============>%s(), cmd:%x\n", __FUNCTION__, mlme->cmd); +// printk("\ndkgadfslkdjgalskdf===============>%s(), cmd:%x\n", __func__, mlme->cmd); #if 1 switch (mlme->cmd) { case IW_MLME_DEAUTH: @@ -830,7 +830,7 @@ int ieee80211_wx_set_auth(struct ieee80211_device *ieee, int ieee80211_wx_set_gen_ie(struct ieee80211_device *ieee, u8 *ie, size_t len) { #if 0 - printk("====>%s()\n", __FUNCTION__); + printk("====>%s()\n", __func__); { int i; for (i=0; iwpa_ie = NULL; ieee->wpa_ie_len = 0; } -// printk("<=====out %s()\n", __FUNCTION__); +// printk("<=====out %s()\n", __func__); return 0; diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c index 00f4df49bc0e..94534955e38b 100644 --- a/drivers/staging/rtl8187se/r8180_core.c +++ b/drivers/staging/rtl8187se/r8180_core.c @@ -1407,12 +1407,12 @@ void rtl8180_set_chan(struct net_device *dev,short ch) if((ch > 14) || (ch < 1)) { - printk("In %s: Invalid chnanel %d\n", __FUNCTION__, ch); + printk("In %s: Invalid chnanel %d\n", __func__, ch); return; } priv->chan=ch; - //printk("in %s:channel is %d\n",__FUNCTION__,ch); + //printk("in %s:channel is %d\n",__func__,ch); priv->rf_set_chan(dev,priv->chan); } @@ -3656,7 +3656,7 @@ void rtl8180_link_change(struct net_device *dev) void rtl8180_rq_tx_ack(struct net_device *dev){ struct r8180_priv *priv = ieee80211_priv(dev); -// printk("====================>%s\n",__FUNCTION__); +// printk("====================>%s\n",__func__); write_nic_byte(dev,CONFIG4,read_nic_byte(dev,CONFIG4)|CONFIG4_PWRMGT); priv->ack_tx_to_ieee = 1; } @@ -5448,7 +5448,7 @@ void rtl8180_hw_wakeup_wq(struct net_device *dev) #endif // printk("dev is %d\n",dev); -// printk("&*&(^*(&(&=========>%s()\n", __FUNCTION__); +// printk("&*&(^*(&(&=========>%s()\n", __func__); rtl8180_hw_wakeup(dev); } @@ -6318,12 +6318,12 @@ priv->txnpring)/8); // printk("NumTxOkTotal is %d\n",priv->NumTxOkTotal++); } #endif - // printk("in function %s:curr_retry_count is %d\n",__FUNCTION__,((*head) & (0x000000ff))); + // printk("in function %s:curr_retry_count is %d\n",__func__,((*head) & (0x000000ff))); } if(!error){ priv->NumTxOkBytesTotal += (*(head+3)) & (0x00000fff); } -// printk("in function %s:curr_txokbyte_count is %d\n",__FUNCTION__,(*(head+3)) & (0x00000fff)); +// printk("in function %s:curr_txokbyte_count is %d\n",__func__,(*(head+3)) & (0x00000fff)); *head = *head &~ (1<<31); if((head - begin)/8 == priv->txringcount-1) diff --git a/drivers/staging/rtl8187se/r8180_rtl8225z2.c b/drivers/staging/rtl8187se/r8180_rtl8225z2.c index 90a574d46da0..4136b9429597 100644 --- a/drivers/staging/rtl8187se/r8180_rtl8225z2.c +++ b/drivers/staging/rtl8187se/r8180_rtl8225z2.c @@ -1571,7 +1571,7 @@ void rtl8225z4_rf_sleep(struct net_device *dev) // // Turn off RF power. // - //printk("=========>%s()\n", __FUNCTION__); + //printk("=========>%s()\n", __func__); MgntActSet_RF_State(dev, eRfSleep, RF_CHANGE_BY_PS); //mdelay(2); //FIXME } @@ -1580,7 +1580,7 @@ void rtl8225z4_rf_wakeup(struct net_device *dev) // // Turn on RF power. // - //printk("=========>%s()\n", __FUNCTION__); + //printk("=========>%s()\n", __func__); MgntActSet_RF_State(dev, eRfOn, RF_CHANGE_BY_PS); } #endif diff --git a/drivers/staging/rtl8187se/r8180_wx.c b/drivers/staging/rtl8187se/r8180_wx.c index c77abe502b79..8b3901d87f87 100644 --- a/drivers/staging/rtl8187se/r8180_wx.c +++ b/drivers/staging/rtl8187se/r8180_wx.c @@ -1177,7 +1177,7 @@ static int r8180_wx_set_channelplan(struct net_device *dev, //struct ieee80211_device *ieee = netdev_priv(dev); int *val = (int *)extra; int i; - printk("-----in fun %s\n", __FUNCTION__); + printk("-----in fun %s\n", __func__); if(priv->ieee80211->bHwRadioOff) return 0; @@ -1235,7 +1235,7 @@ static int r8180_wx_set_forcerate(struct net_device *dev, down(&priv->wx_sem); - printk("==============>%s(): forcerate is %d\n",__FUNCTION__,forcerate); + printk("==============>%s(): forcerate is %d\n",__func__,forcerate); if((forcerate == 2) || (forcerate == 4) || (forcerate == 11) || (forcerate == 22) || (forcerate == 12) || (forcerate == 18) || (forcerate == 24) || (forcerate == 36) || (forcerate == 48) || (forcerate == 72) || (forcerate == 96) || (forcerate == 108)) @@ -1260,7 +1260,7 @@ static int r8180_wx_set_enc_ext(struct net_device *dev, { struct r8180_priv *priv = ieee80211_priv(dev); - //printk("===>%s()\n", __FUNCTION__); + //printk("===>%s()\n", __func__); int ret=0; @@ -1277,7 +1277,7 @@ static int r8180_wx_set_auth(struct net_device *dev, struct iw_request_info *info, struct iw_param *data, char *extra) { - //printk("====>%s()\n", __FUNCTION__); + //printk("====>%s()\n", __func__); struct r8180_priv *priv = ieee80211_priv(dev); int ret=0; @@ -1294,7 +1294,7 @@ static int r8180_wx_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { - //printk("====>%s()\n", __FUNCTION__); + //printk("====>%s()\n", __func__); int ret=0; struct r8180_priv *priv = ieee80211_priv(dev); @@ -1315,7 +1315,7 @@ static int r8180_wx_set_gen_ie(struct net_device *dev, struct iw_request_info *info, struct iw_point *data, char *extra) { -// printk("====>%s(), len:%d\n", __FUNCTION__, data->length); +// printk("====>%s(), len:%d\n", __func__, data->length); int ret=0; struct r8180_priv *priv = ieee80211_priv(dev); @@ -1328,7 +1328,7 @@ static int r8180_wx_set_gen_ie(struct net_device *dev, ret = ieee80211_wx_set_gen_ie(priv->ieee80211, extra, data->length); #endif up(&priv->wx_sem); - //printk("<======%s(), ret:%d\n", __FUNCTION__, ret); + //printk("<======%s(), ret:%d\n", __func__, ret); return ret; -- cgit v1.2.3-59-g8ed1b