Staging: Add the Meilhaus ME-IDS driver package

Originally written by Guenter Gebhardt <g.gebhardt@meilhaus.de>
and Krzysztof Gantzke <k.gantzke@meilhaus.de>

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

Signed-off-by: David Kiliani <mail@davidkiliani.de>
Cc: Guenter Gebhardt <g.gebhardt@meilhaus.de>
Cc: Krzysztof Gantzke <k.gantzke@meilhaus.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
David Kiliani 2008-11-01 00:39:12 +01:00 committed by Greg Kroah-Hartman
parent 5ec5ec7806
commit 3fedd14818
133 changed files with 41371 additions and 0 deletions

View File

@ -49,6 +49,8 @@ source "drivers/staging/sxg/Kconfig"
source "drivers/staging/me4000/Kconfig"
source "drivers/staging/meilhaus/Kconfig"
source "drivers/staging/go7007/Kconfig"
source "drivers/staging/usbip/Kconfig"

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_ET131X) += et131x/
obj-$(CONFIG_SLICOSS) += slicoss/
obj-$(CONFIG_SXG) += sxg/
obj-$(CONFIG_ME4000) += me4000/
obj-$(CONFIG_MEILHAUS) += meilhaus/
obj-$(CONFIG_VIDEO_GO7007) += go7007/
obj-$(CONFIG_USB_IP_COMMON) += usbip/
obj-$(CONFIG_W35UND) += winbond/

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,10 @@
TODO:
- checkpatch.pl cleanups
- sparse issues
- Lindent
- audit userspace interface
- handle firmware properly
- possible comedi merge
Please send cleanup patches to Greg Kroah-Hartman <greg@kroah.com>
and CC: David Kiliani <mail@davidkiliani.de>

View File

@ -0,0 +1,215 @@
/**
* @file me0600_device.c
*
* @brief ME-630 device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "medevice.h"
#include "me0600_device.h"
#include "mesubdevice.h"
#include "me0600_relay.h"
#include "me0600_ttli.h"
#include "me0600_optoi.h"
#include "me0600_dio.h"
#include "me0600_ext_irq.h"
me_device_t *me0600_pci_constructor(struct pci_dev *pci_device)
{
me0600_device_t *me0600_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
int err;
int i;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me0600_device = kmalloc(sizeof(me0600_device_t), GFP_KERNEL);
if (!me0600_device) {
PERROR("Cannot get memory for device instance.\n");
return NULL;
}
memset(me0600_device, 0, sizeof(me0600_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me0600_device, pci_device);
if (err) {
kfree(me0600_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
/* Get the index in the device version information table. */
version_idx =
me0600_versions_get_device_index(me0600_device->base.info.pci.
device_id);
// Initialize spin lock .
spin_lock_init(&me0600_device->dio_ctrl_reg_lock);
spin_lock_init(&me0600_device->intcsr_lock);
// Create subdevice instances.
for (i = 0; i < me0600_versions[version_idx].optoi_subdevices; i++) {
subdevice =
(me_subdevice_t *) me0600_optoi_constructor(me0600_device->
base.info.pci.
reg_bases[2]);
if (!subdevice) {
me_device_deinit((me_device_t *) me0600_device);
kfree(me0600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0600_device->base.slist,
subdevice);
}
for (i = 0; i < me0600_versions[version_idx].relay_subdevices; i++) {
subdevice =
(me_subdevice_t *) me0600_relay_constructor(me0600_device->
base.info.pci.
reg_bases[2]);
if (!subdevice) {
me_device_deinit((me_device_t *) me0600_device);
kfree(me0600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0600_device->base.slist,
subdevice);
}
for (i = 0; i < me0600_versions[version_idx].ttli_subdevices; i++) {
subdevice =
(me_subdevice_t *) me0600_ttli_constructor(me0600_device->
base.info.pci.
reg_bases[2]);
if (!subdevice) {
me_device_deinit((me_device_t *) me0600_device);
kfree(me0600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0600_device->base.slist,
subdevice);
}
for (i = 0; i < me0600_versions[version_idx].dio_subdevices; i++) {
subdevice =
(me_subdevice_t *) me0600_dio_constructor(me0600_device->
base.info.pci.
reg_bases[2], i,
&me0600_device->
dio_ctrl_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me0600_device);
kfree(me0600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0600_device->base.slist,
subdevice);
}
for (i = 0; i < me0600_versions[version_idx].ext_irq_subdevices; i++) {
subdevice =
(me_subdevice_t *)
me0600_ext_irq_constructor(me0600_device->base.info.pci.
reg_bases[1],
me0600_device->base.info.pci.
reg_bases[2],
&me0600_device->intcsr_lock, i,
me0600_device->base.irq);
if (!subdevice) {
me_device_deinit((me_device_t *) me0600_device);
kfree(me0600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0600_device->base.slist,
subdevice);
}
return (me_device_t *) me0600_device;
}
// Init and exit of module.
static int __init me0600_init(void)
{
PDEBUG("executed.\n");
return 0;
}
static void __exit me0600_exit(void)
{
PDEBUG("executed.\n");
}
module_init(me0600_init);
module_exit(me0600_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for ME-6xx Device");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-6xx Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me0600_pci_constructor);

View File

@ -0,0 +1,97 @@
/**
* @file me0600_device.h
*
* @brief ME-630 device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME0600_DEVICE_H
#define _ME0600_DEVICE_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
/**
* @brief Structure holding ME-630 device capabilities.
*/
typedef struct me0600_version {
uint16_t device_id;
unsigned int relay_subdevices;
unsigned int ttli_subdevices;
unsigned int optoi_subdevices;
unsigned int dio_subdevices;
unsigned int ext_irq_subdevices;
} me0600_version_t;
/**
* @brief Device capabilities.
*/
static me0600_version_t me0600_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME0630, 1, 1, 1, 2, 2},
{0},
};
#define ME0600_DEVICE_VERSIONS (sizeof(me0600_versions) / sizeof(me0600_version_t) - 1) /**< Returns the number of entries in #me0600_versions. */
/**
* @brief Returns the index of the device entry in #me0600_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me0600_versions.
*/
static inline unsigned int me0600_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME0600_DEVICE_VERSIONS; i++)
if (me0600_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-630 device class structure.
*/
typedef struct me0600_device {
me_device_t base; /**< The Meilhaus device base class. */
/* Child class attributes. */
spinlock_t dio_ctrl_reg_lock;
spinlock_t intcsr_lock;
} me0600_device_t;
/**
* @brief The ME-630 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-630 device instance. \n
* NULL on error.
*/
me_device_t *me0600_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

View File

@ -0,0 +1,415 @@
/**
* @file me0600_dio.c
*
* @brief ME-630 digital input/output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me0600_dio_reg.h"
#include "me0600_dio.h"
/*
* Defines
*/
/*
* Functions
*/
static int me0600_dio_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me0600_dio_subdevice_t *instance;
uint8_t mode;
PDEBUG("executed.\n");
instance = (me0600_dio_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inb(instance->ctrl_reg);
mode &= ~(0x3 << (instance->dio_idx * 2));
outb(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
spin_unlock(instance->ctrl_reg_lock);
outb(0x00, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0x00);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me0600_dio_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me0600_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
int size =
flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
| ME_IO_SINGLE_CONFIG_DIO_WORD |
ME_IO_SINGLE_CONFIG_DIO_DWORD);
PDEBUG("executed.\n");
instance = (me0600_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inb(instance->ctrl_reg);
switch (size) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
mode &=
~((ME0600_DIO_CONFIG_BIT_OUT_0) <<
(instance->dio_idx * 2));
} else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
mode &=
~((ME0600_DIO_CONFIG_BIT_OUT_0) <<
(instance->dio_idx * 2));
mode |=
ME0600_DIO_CONFIG_BIT_OUT_0 << (instance->
dio_idx *
2);
} else {
PERROR
("Invalid port configuration specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outb(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_dio_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me0600_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
PDEBUG("executed.\n");
instance = (me0600_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inb(instance->
ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
(instance->dio_idx * 2));
if ((mode ==
(ME0600_DIO_CONFIG_BIT_OUT_0 <<
(instance->dio_idx * 2))) || !mode) {
*value =
inb(instance->
port_reg) & (0x0001 << channel);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inb(instance->
ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
(instance->dio_idx * 2));
if ((mode ==
(ME0600_DIO_CONFIG_BIT_OUT_0 <<
(instance->dio_idx * 2))) || !mode) {
*value = inb(instance->port_reg) & 0x00FF;
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
break;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_dio_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me0600_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
uint8_t byte;
PDEBUG("executed.\n");
instance = (me0600_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inb(instance->
ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
(instance->dio_idx * 2));
if (mode ==
(ME0600_DIO_CONFIG_BIT_OUT_0 <<
(instance->dio_idx * 2))) {
byte = inb(instance->port_reg);
if (value)
byte |= 0x1 << channel;
else
byte &= ~(0x1 << channel);
outb(byte, instance->port_reg);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inb(instance->
ctrl_reg) & ((ME0600_DIO_CONFIG_BIT_OUT_0) <<
(instance->dio_idx * 2));
if (mode ==
(ME0600_DIO_CONFIG_BIT_OUT_0 <<
(instance->dio_idx * 2))) {
outb(value, instance->port_reg);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
break;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_dio_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me0600_dio_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DIO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0600_dio_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_DIR_BYTE;
return ME_ERRNO_SUCCESS;
}
me0600_dio_subdevice_t *me0600_dio_constructor(uint32_t reg_base,
unsigned int dio_idx,
spinlock_t * ctrl_reg_lock)
{
me0600_dio_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0600_dio_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0600_dio_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save digital i/o index */
subdevice->dio_idx = dio_idx;
/* Save the subdevice index */
subdevice->ctrl_reg = reg_base + ME0600_DIO_CONFIG_REG;
subdevice->port_reg = reg_base + ME0600_DIO_PORT_REG + dio_idx;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me0600_dio_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me0600_dio_io_single_config;
subdevice->base.me_subdevice_io_single_read = me0600_dio_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me0600_dio_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me0600_dio_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0600_dio_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0600_dio_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,478 @@
/**
* @file me0600_ext_irq.c
*
* @brief ME-630 external interrupt subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "meids.h"
#include "medebug.h"
#include "meplx_reg.h"
#include "me0600_ext_irq_reg.h"
#include "me0600_ext_irq.h"
/*
* Functions
*/
static int me0600_ext_irq_io_irq_start(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags)
{
me0600_ext_irq_subdevice_t *instance;
uint32_t tmp;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me0600_ext_irq_subdevice_t *) subdevice;
if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (instance->lintno > 1) {
PERROR("Wrong idx=%d.\n", instance->lintno);
return ME_ERRNO_INVALID_SUBDEVICE;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
PERROR("Invalid irq source specified.\n");
return ME_ERRNO_INVALID_IRQ_SOURCE;
}
if (irq_edge != ME_IRQ_EDGE_RISING) {
PERROR("Invalid irq edge specified.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->intcsr_lock);
tmp = inl(instance->intcsr);
switch (instance->lintno) {
case 0:
tmp |=
PLX_INTCSR_LOCAL_INT1_EN | PLX_INTCSR_LOCAL_INT1_POL |
PLX_INTCSR_PCI_INT_EN;
break;
case 1:
tmp |=
PLX_INTCSR_LOCAL_INT2_EN | PLX_INTCSR_LOCAL_INT2_POL |
PLX_INTCSR_PCI_INT_EN;
break;
}
outl(tmp, instance->intcsr);
PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
spin_unlock(instance->intcsr_lock);
instance->rised = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me0600_ext_irq_io_irq_wait(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags)
{
me0600_ext_irq_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
long t = 0;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me0600_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (time_out < 0) {
PERROR("Invalid time_out specified.\n");
return ME_ERRNO_INVALID_TIMEOUT;
}
if (time_out) {
t = (time_out * HZ) / 1000;
if (t == 0)
t = 1;
}
ME_SUBDEVICE_ENTER;
if (instance->rised <= 0) {
instance->rised = 0;
if (time_out) {
t = wait_event_interruptible_timeout(instance->
wait_queue,
(instance->rised !=
0), t);
if (t == 0) {
PERROR("Wait on interrupt timed out.\n");
err = ME_ERRNO_TIMEOUT;
}
} else {
wait_event_interruptible(instance->wait_queue,
(instance->rised != 0));
}
if (instance->rised < 0) {
PERROR("Wait on interrupt aborted by user.\n");
err = ME_ERRNO_CANCELLED;
}
}
if (signal_pending(current)) {
PERROR("Wait on interrupt aborted by signal.\n");
err = ME_ERRNO_SIGNAL;
}
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
instance->rised = 0;
*irq_count = instance->n;
*value = 1;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_ext_irq_io_irq_stop(struct me_subdevice *subdevice,
struct file *filep,
int channel, int flags)
{
me0600_ext_irq_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t tmp;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me0600_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (instance->lintno > 1) {
PERROR("Wrong idx=%d.\n", instance->lintno);
return ME_ERRNO_INVALID_SUBDEVICE;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->intcsr_lock);
tmp = inl(instance->intcsr);
switch (instance->lintno) {
case 0:
tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
break;
case 1:
tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
break;
}
outl(tmp, instance->intcsr);
PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
spin_unlock(instance->intcsr_lock);
instance->rised = -1;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_ext_irq_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me0600_ext_irq_subdevice_t *instance;
uint32_t tmp;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me0600_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->intcsr_lock);
tmp = inl(instance->intcsr);
switch (instance->lintno) {
case 0:
tmp |= PLX_INTCSR_LOCAL_INT1_POL | PLX_INTCSR_PCI_INT_EN;
tmp &= ~PLX_INTCSR_LOCAL_INT1_EN;
break;
case 1:
tmp |= PLX_INTCSR_LOCAL_INT2_POL | PLX_INTCSR_PCI_INT_EN;
tmp &= ~PLX_INTCSR_LOCAL_INT2_EN;
break;
}
outl(tmp, instance->intcsr);
PDEBUG_REG("intcsr outl(plx:0x%X)=0x%x\n", instance->intcsr, tmp);
spin_unlock(instance->intcsr_lock);
instance->rised = -1;
instance->n = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me0600_ext_irq_query_number_channels(struct me_subdevice *subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 1;
return ME_ERRNO_SUCCESS;
}
static int me0600_ext_irq_query_subdevice_type(struct me_subdevice *subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_EXT_IRQ;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0600_ext_irq_query_subdevice_caps(struct me_subdevice *subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_EXT_IRQ_EDGE_RISING;
return ME_ERRNO_SUCCESS;
}
static void me0600_ext_irq_destructor(struct me_subdevice *subdevice)
{
me0600_ext_irq_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me0600_ext_irq_subdevice_t *) subdevice;
free_irq(instance->irq, (void *)instance);
me_subdevice_deinit(&instance->base);
kfree(instance);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me0600_isr(int irq, void *dev_id)
#else
static irqreturn_t me0600_isr(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
me0600_ext_irq_subdevice_t *instance;
uint32_t status;
uint32_t mask = PLX_INTCSR_PCI_INT_EN;
irqreturn_t ret = IRQ_HANDLED;
instance = (me0600_ext_irq_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
PDEBUG("executed.\n");
if (instance->lintno > 1) {
PERROR_CRITICAL
("%s():Wrong subdevice index=%d plx:irq_status_reg=0x%04X.\n",
__FUNCTION__, instance->lintno, inl(instance->intcsr));
return IRQ_NONE;
}
spin_lock(&instance->subdevice_lock);
spin_lock(instance->intcsr_lock);
status = inl(instance->intcsr);
switch (instance->lintno) {
case 0:
mask |= PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_LOCAL_INT1_EN;
break;
case 1:
mask |= PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_LOCAL_INT2_EN;
break;
}
if ((status & mask) == mask) {
instance->rised = 1;
instance->n++;
inb(instance->reset_reg);
PDEBUG("Interrupt detected.\n");
} else {
PINFO
("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, status);
ret = IRQ_NONE;
}
spin_unlock(instance->intcsr_lock);
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return ret;
}
me0600_ext_irq_subdevice_t *me0600_ext_irq_constructor(uint32_t plx_reg_base,
uint32_t me0600_reg_base,
spinlock_t * intcsr_lock,
unsigned ext_irq_idx,
int irq)
{
me0600_ext_irq_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0600_ext_irq_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for 630_ext_irq instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0600_ext_irq_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->intcsr_lock = intcsr_lock;
/* Initialize wait queue */
init_waitqueue_head(&subdevice->wait_queue);
subdevice->lintno = ext_irq_idx;
/* Request interrupt line */
subdevice->irq = irq;
err = request_irq(subdevice->irq, me0600_isr,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME0600_NAME, (void *)subdevice);
if (err) {
PERROR("Cannot get interrupt line.\n");
kfree(subdevice);
return NULL;
}
PINFO("Registered irq=%d.\n", subdevice->irq);
/* Initialize registers */
subdevice->intcsr = plx_reg_base + PLX_INTCSR;
subdevice->reset_reg =
me0600_reg_base + ME0600_INT_0_RESET_REG + ext_irq_idx;
/* Initialize the subdevice methods */
subdevice->base.me_subdevice_io_irq_start = me0600_ext_irq_io_irq_start;
subdevice->base.me_subdevice_io_irq_wait = me0600_ext_irq_io_irq_wait;
subdevice->base.me_subdevice_io_irq_stop = me0600_ext_irq_io_irq_stop;
subdevice->base.me_subdevice_io_reset_subdevice =
me0600_ext_irq_io_reset_subdevice;
subdevice->base.me_subdevice_query_number_channels =
me0600_ext_irq_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0600_ext_irq_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0600_ext_irq_query_subdevice_caps;
subdevice->base.me_subdevice_destructor = me0600_ext_irq_destructor;
subdevice->rised = 0;
subdevice->n = 0;
return subdevice;
}

View File

@ -0,0 +1,58 @@
/**
* @file me0600_ext_irq.h
*
* @brief ME-630 external interrupt implementation.
* @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
#ifndef _ME0600_EXT_IRQ_H_
#define _ME0600_EXT_IRQ_H_
#include <linux/sched.h>
#include "mesubdevice.h"
#include "meslock.h"
#ifdef __KERNEL__
/**
* @brief The ME-630 external interrupt subdevice class.
*/
typedef struct me0600_ext_irq_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
/* Attributes */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *intcsr_lock; /**< Spin lock to protect #intcsr. */
wait_queue_head_t wait_queue; /**< Queue to put on threads waiting for an interrupt. */
int irq; /**< The irq number assigned by PCI BIOS. */
int rised; /**< If true an interrupt has occured. */
unsigned int n; /**< The number of interrupt since the driver was loaded. */
unsigned int lintno; /**< The number of the local PCI interrupt. */
uint32_t intcsr; /**< The PLX interrupt control and status register. */
uint32_t reset_reg; /**< The control register. */
} me0600_ext_irq_subdevice_t;
/**
* @brief The constructor to generate a ME-630 external interrupt instance.
*
* @param plx_reg_base The register base address of the PLX chip as returned by the PCI BIOS.
* @param me0600_reg_base The register base address of the ME-630 device as returned by the PCI BIOS.
* @param irq The irq assigned by the PCI BIOS.
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me0600_ext_irq_subdevice_t *me0600_ext_irq_constructor(uint32_t plx_reg_base,
uint32_t me0600_reg_base,
spinlock_t * intcsr_lock,
unsigned int ext_irq_idx,
int irq);
#endif
#endif

View File

@ -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

View File

@ -0,0 +1,243 @@
/**
* @file me0600_optoi.c
*
* @brief ME-630 Optoisolated input subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me0600_optoi_reg.h"
#include "me0600_optoi.h"
/*
* Defines
*/
/*
* Functions
*/
static int me0600_optoi_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
PDEBUG("executed.\n");
return ME_ERRNO_SUCCESS;
}
static int me0600_optoi_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type,
int trig_edge, int flags)
{
me0600_optoi_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0600_optoi_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config != ME_SINGLE_CONFIG_DIO_INPUT) {
PERROR("Invalid port direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
break;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_optoi_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me0600_optoi_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0600_optoi_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inb(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inb(instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_optoi_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me0600_optoi_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DI;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0600_optoi_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me0600_optoi_subdevice_t *me0600_optoi_constructor(uint32_t reg_base)
{
me0600_optoi_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0600_optoi_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0600_optoi_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
/* Save the subdevice index */
subdevice->port_reg = reg_base + ME0600_OPTO_INPUT_REG;
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me0600_optoi_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me0600_optoi_io_single_config;
subdevice->base.me_subdevice_io_single_read =
me0600_optoi_io_single_read;
subdevice->base.me_subdevice_query_number_channels =
me0600_optoi_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0600_optoi_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0600_optoi_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,359 @@
/**
* @file me0600_relay.c
*
* @brief ME-630 relay subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me0600_relay_reg.h"
#include "me0600_relay.h"
/*
* Defines
*/
/*
* Functions
*/
static int me0600_relay_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me0600_relay_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me0600_relay_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
outb(0x0, instance->port_0_reg);
PDEBUG_REG("port_0_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_0_reg - instance->reg_base, 0);
outb(0x0, instance->port_1_reg);
PDEBUG_REG("port_1_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_1_reg - instance->reg_base, 0);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me0600_relay_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type,
int trig_edge, int flags)
{
me0600_relay_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0600_relay_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_WORD:
if (channel == 0) {
if (single_config != ME_SINGLE_CONFIG_DIO_OUTPUT) {
PERROR("Invalid word direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
break;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_relay_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me0600_relay_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0600_relay_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inb(instance->port_0_reg) & (0x1 << channel);
} else if ((channel >= 8) && (channel < 16)) {
*value =
inb(instance->port_1_reg) & (0x1 << (channel - 8));
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inb(instance->port_0_reg);
} else if (channel == 1) {
*value = inb(instance->port_1_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_WORD:
if (channel == 0) {
*value = (uint32_t) inb(instance->port_1_reg) << 8;
*value |= inb(instance->port_0_reg);
} else {
PERROR("Invalid word number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_relay_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me0600_relay_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t state;
PDEBUG("executed.\n");
instance = (me0600_relay_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
state = inb(instance->port_0_reg);
state =
value ? (state | (0x1 << channel)) : (state &
~(0x1 <<
channel));
outb(state, instance->port_0_reg);
} else if ((channel >= 8) && (channel < 16)) {
state = inb(instance->port_1_reg);
state =
value ? (state | (0x1 << (channel - 8))) : (state &
~(0x1 <<
(channel
-
8)));
outb(state, instance->port_1_reg);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
outb(value, instance->port_0_reg);
} else if (channel == 1) {
outb(value, instance->port_1_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_WORD:
if (channel == 0) {
outb(value, instance->port_0_reg);
outb(value >> 8, instance->port_1_reg);
} else {
PERROR("Invalid word number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
break;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_relay_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 16;
return ME_ERRNO_SUCCESS;
}
static int me0600_relay_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0600_relay_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me0600_relay_subdevice_t *me0600_relay_constructor(uint32_t reg_base)
{
me0600_relay_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0600_relay_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0600_relay_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
/* Save the subdevice index */
subdevice->port_0_reg = reg_base + ME0600_RELAIS_0_REG;
subdevice->port_1_reg = reg_base + ME0600_RELAIS_1_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me0600_relay_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me0600_relay_io_single_config;
subdevice->base.me_subdevice_io_single_read =
me0600_relay_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me0600_relay_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me0600_relay_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0600_relay_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0600_relay_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,238 @@
/**
* @file me0600_ttli.c
*
* @brief ME-630 TTL input subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me0600_ttli_reg.h"
#include "me0600_ttli.h"
/*
* Defines
*/
/*
* Functions
*/
static int me0600_ttli_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
PDEBUG("executed.\n");
return ME_ERRNO_SUCCESS;
}
static int me0600_ttli_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me0600_ttli_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0600_ttli_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config != ME_SINGLE_CONFIG_DIO_INPUT) {
PERROR("Invalid port direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
break;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_ttli_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me0600_ttli_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0600_ttli_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inb(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inb(instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0600_ttli_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me0600_ttli_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DI;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0600_ttli_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me0600_ttli_subdevice_t *me0600_ttli_constructor(uint32_t reg_base)
{
me0600_ttli_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0600_ttli_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0600_ttli_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
/* Save the subdevice index */
subdevice->port_reg = reg_base + ME0600_TTL_INPUT_REG;
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me0600_ttli_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me0600_ttli_io_single_config;
subdevice->base.me_subdevice_io_single_read =
me0600_ttli_io_single_read;
subdevice->base.me_subdevice_query_number_channels =
me0600_ttli_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0600_ttli_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0600_ttli_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,180 @@
/**
* @file me0900_device.c
*
* @brief ME-9x device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "medevice.h"
#include "me0900_device.h"
#include "me0900_reg.h"
#include "mesubdevice.h"
#include "me0900_do.h"
#include "me0900_di.h"
me_device_t *me0900_pci_constructor(struct pci_dev *pci_device)
{
me0900_device_t *me0900_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
int err;
int i;
int port_shift;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me0900_device = kmalloc(sizeof(me0900_device_t), GFP_KERNEL);
if (!me0900_device) {
PERROR("Cannot get memory for device instance.\n");
return NULL;
}
memset(me0900_device, 0, sizeof(me0900_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me0900_device, pci_device);
if (err) {
kfree(me0900_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
/* Get the index in the device version information table. */
version_idx =
me0900_versions_get_device_index(me0900_device->base.info.pci.
device_id);
/* Initialize 8255 chip to desired mode */
if (me0900_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME0940) {
outb(0x9B,
me0900_device->base.info.pci.reg_bases[2] +
ME0900_CTRL_REG);
} else if (me0900_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME0950) {
outb(0x89,
me0900_device->base.info.pci.reg_bases[2] +
ME0900_CTRL_REG);
outb(0x00,
me0900_device->base.info.pci.reg_bases[2] +
ME0900_WRITE_ENABLE_REG);
} else if (me0900_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME0960) {
outb(0x8B,
me0900_device->base.info.pci.reg_bases[2] +
ME0900_CTRL_REG);
outb(0x00,
me0900_device->base.info.pci.reg_bases[2] +
ME0900_WRITE_ENABLE_REG);
}
port_shift =
(me0900_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME0960) ? 1 : 0;
// Create subdevice instances.
for (i = 0; i < me0900_versions[version_idx].di_subdevices; i++) {
subdevice =
(me_subdevice_t *) me0900_di_constructor(me0900_device->
base.info.pci.
reg_bases[2],
i + port_shift);
if (!subdevice) {
me_device_deinit((me_device_t *) me0900_device);
kfree(me0900_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0900_device->base.slist,
subdevice);
}
for (i = 0; i < me0900_versions[version_idx].do_subdevices; i++) {
subdevice =
(me_subdevice_t *) me0900_do_constructor(me0900_device->
base.info.pci.
reg_bases[2], i);
if (!subdevice) {
me_device_deinit((me_device_t *) me0900_device);
kfree(me0900_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me0900_device->base.slist,
subdevice);
}
return (me_device_t *) me0900_device;
}
// Init and exit of module.
static int __init me0900_init(void)
{
PDEBUG("executed.\n.");
return 0;
}
static void __exit me0900_exit(void)
{
PDEBUG("executed.\n.");
}
module_init(me0900_init);
module_exit(me0900_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for ME-9x Device");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-9x Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me0900_pci_constructor);

View File

@ -0,0 +1,92 @@
/**
* @file me0900_device.h
*
* @brief ME-0900 (ME-9x) device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME0900_DEVICE_H
#define _ME0900_DEVICE_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
/**
* @brief Structure holding ME-0900 (ME-9x) device capabilities.
*/
typedef struct me0900_version {
uint16_t device_id;
unsigned int di_subdevices;
unsigned int do_subdevices;
} me0900_version_t;
/**
* @brief Device capabilities.
*/
static me0900_version_t me0900_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME0940, 2, 0},
{PCI_DEVICE_ID_MEILHAUS_ME0950, 0, 2},
{PCI_DEVICE_ID_MEILHAUS_ME0960, 1, 1},
{0},
};
#define ME0900_DEVICE_VERSIONS (sizeof(me0900_versions) / sizeof(me0900_version_t) - 1) /**< Returns the number of entries in #me0900_versions. */
/**
* @brief Returns the index of the device entry in #me0900_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me0900_versions.
*/
static inline unsigned int me0900_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME0900_DEVICE_VERSIONS; i++)
if (me0900_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-0900 (ME-9x) device class structure.
*/
typedef struct me0900_device {
me_device_t base; /**< The Meilhaus device base class. */
} me0900_device_t;
/**
* @brief The ME-9x device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-0900 (ME-9x) device instance. \n
* NULL on error.
*/
me_device_t *me0900_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

View File

@ -0,0 +1,246 @@
/**
* @file me0900_di.c
*
* @brief ME-9x digital input subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/version.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "meids.h"
#include "medebug.h"
#include "meplx_reg.h"
#include "me0900_reg.h"
#include "me0900_di.h"
/*
* Defines
*/
/*
* Functions
*/
static int me0900_di_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
PDEBUG("executed.\n");
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
return ME_ERRNO_SUCCESS;
}
static int me0900_di_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me0900_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0900_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
} else {
PERROR("Invalid byte direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0900_di_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me0900_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0900_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = (~inb(instance->port_reg)) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = ~inb(instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0900_di_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me0900_di_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DI;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0900_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me0900_di_subdevice_t *me0900_di_constructor(uint32_t reg_base,
unsigned int di_idx)
{
me0900_di_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0900_di_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0900_di_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
/* Save the subdevice index. */
subdevice->di_idx = di_idx;
/* Initialize registers */
if (di_idx == 0) {
subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
subdevice->port_reg = reg_base + ME0900_PORT_A_REG;
} else {
subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
subdevice->port_reg = reg_base + ME0900_PORT_B_REG;
}
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me0900_di_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me0900_di_io_single_config;
subdevice->base.me_subdevice_io_single_read = me0900_di_io_single_read;
subdevice->base.me_subdevice_query_number_channels =
me0900_di_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0900_di_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0900_di_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -0,0 +1,314 @@
/**
* @file me0900_do.c
*
* @brief ME-9x digital output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me0900_reg.h"
#include "me0900_do.h"
/*
* Defines
*/
/*
* Functions
*/
static int me0900_do_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me0900_do_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me0900_do_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
outb(0xFF, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0xff);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me0900_do_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me0900_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0900_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
} else {
PERROR("Invalid byte direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0900_do_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me0900_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me0900_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = (~inb(instance->port_reg)) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = ~inb(instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0900_do_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me0900_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long state;
PDEBUG("executed.\n");
instance = (me0900_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
state = inb(instance->port_reg);
state =
(!value) ? (state | (0x1 << channel)) : (state &
~(0x1 <<
channel));
outb(state, instance->port_reg);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
outb(~(value), instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me0900_do_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me0900_do_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me0900_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me0900_do_subdevice_t *me0900_do_constructor(uint32_t reg_base,
unsigned int do_idx)
{
me0900_do_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me0900_do_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me0900_do_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
/* Save the subdevice index */
subdevice->do_idx = do_idx;
/* Initialize registers */
if (do_idx == 0) {
subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
subdevice->port_reg = reg_base + ME0900_PORT_A_REG;
subdevice->enable_reg = reg_base + ME0900_WRITE_ENABLE_REG;
subdevice->disable_reg = reg_base + ME0900_WRITE_DISABLE_REG;
} else {
subdevice->ctrl_reg = reg_base + ME0900_CTRL_REG;
subdevice->port_reg = reg_base + ME0900_PORT_B_REG;
subdevice->enable_reg = reg_base + ME0900_WRITE_ENABLE_REG;
subdevice->disable_reg = reg_base + ME0900_WRITE_DISABLE_REG;
}
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me0900_do_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me0900_do_io_single_config;
subdevice->base.me_subdevice_io_single_read = me0900_do_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me0900_do_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me0900_do_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me0900_do_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me0900_do_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,208 @@
/**
* @file me1000_device.c
*
* @brief ME-1000 device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "medevice.h"
#include "me1000_device.h"
#include "mesubdevice.h"
#include "me1000_dio.h"
static int me1000_config_load(me_device_t * me_device, struct file *filep,
me_cfg_device_entry_t * config)
{
me1000_device_t *me1000_device;
me1000_dio_subdevice_t *dio;
PDEBUG("executed.\n");
me1000_device = (me1000_device_t *) me_device;
if (config->count == 2) {
if (me_slist_get_number_subdevices(&me1000_device->base.slist)
== 2) {
// Nothing to do.
} else {
// Remove 2 extra subdevices
dio =
(me1000_dio_subdevice_t *)
me_slist_del_subdevice_tail(&me1000_device->base.
slist);
if (dio)
dio->base.
me_subdevice_destructor((me_subdevice_t *)
dio);
dio =
(me1000_dio_subdevice_t *)
me_slist_del_subdevice_tail(&me1000_device->base.
slist);
if (dio)
dio->base.
me_subdevice_destructor((me_subdevice_t *)
dio);
}
} else if (config->count == 4) {
//Add 2 subdevices
if (me_slist_get_number_subdevices(&me1000_device->base.slist)
== 2) {
dio =
me1000_dio_constructor(me1000_device->base.info.pci.
reg_bases[2], 2,
&me1000_device->ctrl_lock);
if (!dio) {
PERROR("Cannot create dio subdevice.\n");
return ME_ERRNO_INTERNAL;
}
me_slist_add_subdevice_tail(&me1000_device->base.slist,
(me_subdevice_t *) dio);
dio =
me1000_dio_constructor(me1000_device->base.info.pci.
reg_bases[2], 3,
&me1000_device->ctrl_lock);
if (!dio) {
dio =
(me1000_dio_subdevice_t *)
me_slist_del_subdevice_tail(&me1000_device->
base.slist);
if (dio)
dio->base.
me_subdevice_destructor((me_subdevice_t *) dio);
PERROR("Cannot create dio subdevice.\n");
return ME_ERRNO_INTERNAL;
}
me_slist_add_subdevice_tail(&me1000_device->base.slist,
(me_subdevice_t *) dio);
} else {
// Nothing to do.
}
} else {
PERROR("Invalid configuration.\n");
return ME_ERRNO_INTERNAL;
}
return ME_ERRNO_SUCCESS;
}
me_device_t *me1000_pci_constructor(struct pci_dev * pci_device)
{
me1000_device_t *me1000_device;
me_subdevice_t *subdevice;
int err;
int i;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me1000_device = kmalloc(sizeof(me1000_device_t), GFP_KERNEL);
if (!me1000_device) {
PERROR("Cannot get memory for ME-1000 device instance.\n");
return NULL;
}
memset(me1000_device, 0, sizeof(me1000_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me1000_device, pci_device);
if (err) {
kfree(me1000_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
// Initialize spin lock .
spin_lock_init(&me1000_device->ctrl_lock);
for (i = 0; i < 4; i++) {
subdevice =
(me_subdevice_t *) me1000_dio_constructor(me1000_device->
base.info.pci.
reg_bases[2], i,
&me1000_device->
ctrl_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me1000_device);
kfree(me1000_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me1000_device->base.slist,
subdevice);
}
// Overwrite base class methods.
me1000_device->base.me_device_config_load = me1000_config_load;
return (me_device_t *) me1000_device;
}
// Init and exit of module.
static int __init me1000_init(void)
{
PDEBUG("executed.\n");
return 0;
}
static void __exit me1000_exit(void)
{
PDEBUG("executed.\n");
}
module_init(me1000_init);
module_exit(me1000_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for Meilhaus ME-1000 Devices");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-1000 Digital I/O Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me1000_pci_constructor);

View File

@ -0,0 +1,59 @@
/**
* @file me1000_device.h
*
* @brief ME-1000 device class instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME1000_H_
#define _ME1000_H_
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
#define ME1000_MAGIC_NUMBER 1000
/**
* @brief The ME-1000 device class structure.
*/
typedef struct me1000_device {
me_device_t base; /**< The Meilhaus device base class. */
spinlock_t ctrl_lock; /**< Guards the DIO mode register. */
} me1000_device_t;
/**
* @brief The ME-1000 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-1000 device instance. \n
* NULL on error.
*/
me_device_t *me1000_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

View File

@ -0,0 +1,438 @@
/**
* @file me1000_dio.c
*
* @brief ME-1000 DIO subdevice instance.
* @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me1000_dio_reg.h"
#include "me1000_dio.h"
/*
* Defines
*/
#define ME1000_DIO_MAGIC_NUMBER 0x1000 /**< The magic number of the class structure. */
/*
* Functions
*/
static int me1000_dio_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me1000_dio_subdevice_t *instance;
uint32_t tmp;
PDEBUG("executed.\n");
instance = (me1000_dio_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
tmp = inl(instance->ctrl_reg);
tmp &= ~(0x1 << instance->dio_idx);
outl(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->ctrl_reg_lock);
outl(0x00000000, instance->port_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, 0);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me1000_dio_io_single_config(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me1000_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
int ctrl;
int size =
flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
| ME_IO_SINGLE_CONFIG_DIO_WORD |
ME_IO_SINGLE_CONFIG_DIO_DWORD);
PDEBUG("executed.\n");
instance = (me1000_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
ctrl = inl(instance->ctrl_reg);
switch (size) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_DWORD:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
ctrl &= ~(0x1 << instance->dio_idx);
} else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
ctrl |= 0x1 << instance->dio_idx;
} else {
PERROR("Invalid port direction.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outl(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me1000_dio_io_single_read(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me1000_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me1000_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 32)) {
*value = inl(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if ((channel >= 0) && (channel < 4)) {
*value =
(inl(instance->port_reg) >> (channel * 8)) & 0xFF;
} else {
PERROR("Invalid byte number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_WORD:
if ((channel >= 0) && (channel < 2)) {
*value =
(inl(instance->port_reg) >> (channel * 16)) &
0xFFFF;
} else {
PERROR("Invalid word number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_DWORD:
if (channel == 0) {
*value = inl(instance->port_reg);
} else {
PERROR("Invalid dword number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me1000_dio_io_single_write(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me1000_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t config;
uint32_t state;
PDEBUG("executed.\n");
instance = (me1000_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
config = inl(instance->ctrl_reg) & (0x1 << instance->dio_idx);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 32)) {
if (config) {
state = inl(instance->port_reg);
state =
value ? (state | (0x1 << channel)) : (state
&
~(0x1
<<
channel));
outl(state, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg -
instance->reg_base, state);
} else {
PERROR("Port is not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if ((channel >= 0) && (channel < 4)) {
if (config) {
state = inl(instance->port_reg);
state &= ~(0xFF << (channel * 8));
state |= (value & 0xFF) << (channel * 8);
outl(state, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg -
instance->reg_base, state);
} else {
PERROR("Port is not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_WORD:
if ((channel >= 0) && (channel < 2)) {
if (config) {
state = inl(instance->port_reg);
state &= ~(0xFFFF << (channel * 16));
state |= (value & 0xFFFF) << (channel * 16);
outl(state, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg -
instance->reg_base, state);
} else {
PERROR("Port is not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid word number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_DWORD:
if (channel == 0) {
if (config) {
outl(value, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg -
instance->reg_base, value);
} else {
PERROR("Port is not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid dword number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me1000_dio_query_number_channels(struct me_subdevice *subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = ME1000_DIO_NUMBER_CHANNELS;
return ME_ERRNO_SUCCESS;
}
static int me1000_dio_query_subdevice_type(struct me_subdevice *subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DIO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me1000_dio_query_subdevice_caps(struct me_subdevice *subdevice,
int *caps)
{
me1000_dio_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me1000_dio_subdevice_t *) subdevice;
*caps = ME_CAPS_DIO_DIR_DWORD;
return ME_ERRNO_SUCCESS;
}
me1000_dio_subdevice_t *me1000_dio_constructor(uint32_t reg_base,
unsigned int dio_idx,
spinlock_t * ctrl_reg_lock)
{
me1000_dio_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me1000_dio_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for ME-1000 DIO instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me1000_dio_subdevice_t));
/* Check if counter index is out of range */
if (dio_idx >= ME1000_DIO_NUMBER_PORTS) {
PERROR("DIO index is out of range.\n");
kfree(subdevice);
return NULL;
}
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save the DIO index */
subdevice->dio_idx = dio_idx;
/* Initialize registers. */
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
subdevice->ctrl_reg = reg_base + ME1000_PORT_MODE;
subdevice->port_reg =
reg_base + ME1000_PORT + (dio_idx * ME1000_PORT_STEP);
/* Override base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me1000_dio_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me1000_dio_io_single_config;
subdevice->base.me_subdevice_io_single_read = me1000_dio_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me1000_dio_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me1000_dio_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me1000_dio_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me1000_dio_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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_

View File

@ -0,0 +1,256 @@
/**
* @file me1400_device.c
*
* @brief ME-1400 device instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* User application could also include the kernel header files. But the
* real kernel functions are protected by #ifdef __KERNEL__.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* This must be defined before module.h is included. Not needed, when
* it is a built in driver.
*/
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/version.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "me1400_device.h"
#include "me8254.h"
#include "me8254_reg.h"
#include "me8255.h"
#include "me1400_ext_irq.h"
me_device_t *me1400_pci_constructor(struct pci_dev *pci_device)
{
int err;
me1400_device_t *me1400_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
unsigned int me8255_idx;
unsigned int dio_idx;
unsigned int me8254_idx;
unsigned int ctr_idx;
unsigned int ext_irq_idx;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me1400_device = kmalloc(sizeof(me1400_device_t), GFP_KERNEL);
if (!me1400_device) {
PERROR("Cannot get memory for 1400ate device instance.\n");
return NULL;
}
memset(me1400_device, 0, sizeof(me1400_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me1400_device, pci_device);
if (err) {
kfree(me1400_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
/* Check for ME1400 extension device. If detected we fake a ME-1400 D device id. */
if (me1400_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME140C) {
uint8_t ctrl;
ctrl =
inb(me1400_device->base.info.pci.reg_bases[2] +
ME1400D_CLK_SRC_2_REG);
PDEBUG_REG("xxx_reg inb(0x%X+0x%X)=0x%x\n",
me1400_device->base.info.pci.reg_bases[2],
ME1400D_CLK_SRC_2_REG, ctrl);
outb(ctrl | 0xF0,
me1400_device->base.info.pci.reg_bases[2] +
ME1400D_CLK_SRC_2_REG);
PDEBUG_REG("xxx_reg outb(0x%X+0x%X)=0x%x\n",
me1400_device->base.info.pci.reg_bases[2],
ME1400D_CLK_SRC_2_REG, ctrl | 0xF0);
ctrl =
inb(me1400_device->base.info.pci.reg_bases[2] +
ME1400D_CLK_SRC_2_REG);
PDEBUG_REG("xxx_reg inb(0x%X+0x%X)=0x%x\n",
me1400_device->base.info.pci.reg_bases[2],
ME1400D_CLK_SRC_2_REG, ctrl);
if ((ctrl & 0xF0) == 0xF0) {
PINFO("ME1400 D detected.\n");
me1400_device->base.info.pci.device_id =
PCI_DEVICE_ID_MEILHAUS_ME140D;
}
}
/* Initialize global stuff of digital i/o subdevices. */
for (me8255_idx = 0; me8255_idx < ME1400_MAX_8255; me8255_idx++) {
me1400_device->dio_current_mode[me8255_idx] = 0;
spin_lock_init(&me1400_device->dio_ctrl_reg_lock[me8255_idx]);
}
/* Initialize global stuff of counter subdevices. */
spin_lock_init(&me1400_device->clk_src_reg_lock);
for (me8254_idx = 0; me8254_idx < ME1400_MAX_8254; me8254_idx++)
spin_lock_init(&me1400_device->ctr_ctrl_reg_lock[me8254_idx]);
/* Get the index in the device version information table. */
version_idx =
me1400_versions_get_device_index(me1400_device->base.info.pci.
device_id);
/* Generate DIO subdevice instances. */
for (me8255_idx = 0;
me8255_idx < me1400_versions[version_idx].dio_chips;
me8255_idx++) {
for (dio_idx = 0; dio_idx < 3; dio_idx++) {
subdevice =
(me_subdevice_t *)
me8255_constructor(me1400_versions[version_idx].
device_id,
me1400_device->base.info.pci.
reg_bases[2], me8255_idx,
dio_idx,
&me1400_device->
dio_current_mode[me8255_idx],
&me1400_device->
dio_ctrl_reg_lock[me8255_idx]);
if (!subdevice) {
me_device_deinit((me_device_t *) me1400_device);
kfree(me1400_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me1400_device->base.slist,
subdevice);
}
}
/* Generate counter subdevice instances. */
for (me8254_idx = 0;
me8254_idx < me1400_versions[version_idx].ctr_chips;
me8254_idx++) {
for (ctr_idx = 0; ctr_idx < 3; ctr_idx++) {
subdevice =
(me_subdevice_t *)
me8254_constructor(me1400_device->base.info.pci.
device_id,
me1400_device->base.info.pci.
reg_bases[2], me8254_idx,
ctr_idx,
&me1400_device->
ctr_ctrl_reg_lock[me8254_idx],
&me1400_device->
clk_src_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me1400_device);
kfree(me1400_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me1400_device->base.slist,
subdevice);
}
}
/* Generate external interrupt subdevice instances. */
for (ext_irq_idx = 0;
ext_irq_idx < me1400_versions[version_idx].ext_irq_subdevices;
ext_irq_idx++) {
subdevice =
(me_subdevice_t *)
me1400_ext_irq_constructor(me1400_device->base.info.pci.
device_id,
me1400_device->base.info.pci.
reg_bases[1],
me1400_device->base.info.pci.
reg_bases[2],
&me1400_device->clk_src_reg_lock,
me1400_device->base.irq);
if (!subdevice) {
me_device_deinit((me_device_t *) me1400_device);
kfree(me1400_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me1400_device->base.slist,
subdevice);
}
return (me_device_t *) me1400_device;
}
// Init and exit of module.
static int __init me1400_init(void)
{
PDEBUG("executed.\n");
return 0;
}
static void __exit me1400_exit(void)
{
PDEBUG("executed.\n");
}
module_init(me1400_init);
module_exit(me1400_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for Meilhaus ME-14xx devices");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-14xx MIO devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me1400_pci_constructor);

View File

@ -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

View File

@ -0,0 +1,517 @@
/**
* @file me1400_ext_irq.c
*
* @brief ME-1400 external interrupt subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "meids.h"
#include "me1400_ext_irq.h"
#include "me1400_ext_irq_reg.h"
/*
* Defines
*/
#define ME1400_EXT_IRQ_MAGIC_NUMBER 0x1401 /**< The magic number of the class structure. */
#define ME1400_EXT_IRQ_NUMBER_CHANNELS 1 /**< One channel per counter. */
/*
* Functions
*/
static int me1400_ext_irq_io_irq_start(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags)
{
me1400_ext_irq_subdevice_t *instance;
unsigned long cpu_flags;
uint8_t tmp;
PDEBUG("executed.\n");
instance = (me1400_ext_irq_subdevice_t *) subdevice;
if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
PERROR("Invalid irq source.\n");
return ME_ERRNO_INVALID_IRQ_SOURCE;
}
if (irq_edge != ME_IRQ_EDGE_RISING) {
PERROR("Invalid irq edge.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->clk_src_reg_lock);
// // Enable IRQ on PLX
// tmp = inb(instance->plx_intcs_reg) | (PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL | PLX_PCI_INT_EN);
// outb(tmp, instance->plx_intcs_reg);
// PDEBUG_REG("ctrl_reg outb(PLX:0x%lX)=0x%x\n", instance->plx_intcs_reg, tmp);
// Enable IRQ
switch (instance->device_id) {
case PCI_DEVICE_ID_MEILHAUS_ME140C:
case PCI_DEVICE_ID_MEILHAUS_ME140D:
tmp = inb(instance->ctrl_reg);
tmp |= ME1400CD_EXT_IRQ_CLK_EN;
outb(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
break;
default:
outb(ME1400AB_EXT_IRQ_IRQ_EN, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base,
ME1400AB_EXT_IRQ_IRQ_EN);
break;
}
spin_unlock(instance->clk_src_reg_lock);
instance->rised = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me1400_ext_irq_io_irq_wait(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags)
{
me1400_ext_irq_subdevice_t *instance;
unsigned long cpu_flags;
long t = 0;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me1400_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (time_out < 0) {
PERROR("Invalid time out.\n");
return ME_ERRNO_INVALID_TIMEOUT;
}
if (time_out) {
/* Convert to ticks */
t = (time_out * HZ) / 1000;
if (t == 0)
t = 1;
}
ME_SUBDEVICE_ENTER;
if (instance->rised <= 0) {
instance->rised = 0;
if (time_out) {
t = wait_event_interruptible_timeout(instance->
wait_queue,
(instance->rised !=
0), t);
if (t == 0) {
PERROR("Wait on interrupt timed out.\n");
err = ME_ERRNO_TIMEOUT;
}
} else {
wait_event_interruptible(instance->wait_queue,
(instance->rised != 0));
}
if (instance->rised < 0) {
PERROR("Wait on interrupt aborted by user.\n");
err = ME_ERRNO_CANCELLED;
}
}
if (signal_pending(current)) {
PERROR("Wait on interrupt aborted by signal.\n");
err = ME_ERRNO_SIGNAL;
}
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
instance->rised = 0;
*irq_count = instance->n;
*value = 1;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me1400_ext_irq_io_irq_stop(struct me_subdevice *subdevice,
struct file *filep,
int channel, int flags)
{
me1400_ext_irq_subdevice_t *instance;
unsigned long cpu_flags;
uint8_t tmp;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me1400_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->clk_src_reg_lock);
// // Disable IRQ on PLX
// tmp = inb(instance->plx_intcs_reg) & ( ~(PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL | PLX_PCI_INT_EN));
// outb(tmp, instance->plx_intcs_reg);
// PDEBUG_REG("ctrl_reg outb(PLX:0x%lX)=0x%x\n", instance->plx_intcs_reg, tmp);
switch (instance->device_id) {
case PCI_DEVICE_ID_MEILHAUS_ME140C:
case PCI_DEVICE_ID_MEILHAUS_ME140D:
tmp = inb(instance->ctrl_reg);
tmp &= ~ME1400CD_EXT_IRQ_CLK_EN;
outb(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
break;
default:
outb(0x00, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, 0x00);
break;
}
spin_unlock(instance->clk_src_reg_lock);
instance->rised = -1;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return err;
}
static int me1400_ext_irq_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me1400_ext_irq_subdevice_t *instance =
(me1400_ext_irq_subdevice_t *) subdevice;
PDEBUG("executed.\n");
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
instance->n = 0;
return me1400_ext_irq_io_irq_stop(subdevice, filep, 0, flags);
}
static int me1400_ext_irq_query_number_channels(struct me_subdevice *subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = ME1400_EXT_IRQ_NUMBER_CHANNELS;
return ME_ERRNO_SUCCESS;
}
static int me1400_ext_irq_query_subdevice_type(struct me_subdevice *subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_EXT_IRQ;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me1400_ext_irq_query_subdevice_caps(struct me_subdevice *subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_EXT_IRQ_EDGE_RISING;
return ME_ERRNO_SUCCESS;
}
static int me1400_ext_irq_query_subdevice_caps_args(struct me_subdevice
*subdevice, int cap,
int *args, int count)
{
PDEBUG("executed.\n");
return ME_ERRNO_NOT_SUPPORTED;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me1400_ext_irq_isr(int irq, void *dev_id)
#else
static irqreturn_t me1400_ext_irq_isr(int irq, void *dev_id,
struct pt_regs *regs)
#endif
{
me1400_ext_irq_subdevice_t *instance;
uint32_t status;
uint8_t tmp;
instance = (me1400_ext_irq_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
spin_lock(&instance->subdevice_lock);
status = inl(instance->plx_intcs_reg);
// if (!((status & PLX_LOCAL_INT1_STATE) && (status & PLX_LOCAL_INT1_EN) && (status & PLX_PCI_INT_EN)))
if ((status &
(PLX_LOCAL_INT1_STATE | PLX_LOCAL_INT1_EN | PLX_PCI_INT_EN)) !=
(PLX_LOCAL_INT1_STATE | PLX_LOCAL_INT1_EN | PLX_PCI_INT_EN)) {
spin_unlock(&instance->subdevice_lock);
PINFO("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, status);
return IRQ_NONE;
}
inl(instance->ctrl_reg);
PDEBUG("executed.\n");
instance->n++;
instance->rised = 1;
switch (instance->device_id) {
case PCI_DEVICE_ID_MEILHAUS_ME140C:
case PCI_DEVICE_ID_MEILHAUS_ME140D:
spin_lock(instance->clk_src_reg_lock);
tmp = inb(instance->ctrl_reg);
tmp &= ~ME1400CD_EXT_IRQ_CLK_EN;
outb(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
tmp |= ME1400CD_EXT_IRQ_CLK_EN;
outb(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->clk_src_reg_lock);
break;
default:
outb(0, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, 0);
outb(ME1400AB_EXT_IRQ_IRQ_EN, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base,
ME1400AB_EXT_IRQ_IRQ_EN);
break;
}
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return IRQ_HANDLED;
}
static void me1400_ext_irq_destructor(struct me_subdevice *subdevice)
{
me1400_ext_irq_subdevice_t *instance;
uint8_t tmp;
PDEBUG("executed.\n");
instance = (me1400_ext_irq_subdevice_t *) subdevice;
// Disable IRQ on PLX
tmp =
inb(instance->
plx_intcs_reg) & (~(PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL |
PLX_PCI_INT_EN));
outb(tmp, instance->plx_intcs_reg);
PDEBUG_REG("ctrl_reg outb(plx:0x%lX)=0x%x\n", instance->plx_intcs_reg,
tmp);
free_irq(instance->irq, (void *)instance);
me_subdevice_deinit(&instance->base);
kfree(instance);
}
me1400_ext_irq_subdevice_t *me1400_ext_irq_constructor(uint32_t device_id,
uint32_t plx_reg_base,
uint32_t me1400_reg_base,
spinlock_t *
clk_src_reg_lock,
int irq)
{
me1400_ext_irq_subdevice_t *subdevice;
int err;
uint8_t tmp;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me1400_ext_irq_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for 1400_ext_irq instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me1400_ext_irq_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->clk_src_reg_lock = clk_src_reg_lock;
/* Initialize wait queue */
init_waitqueue_head(&subdevice->wait_queue);
subdevice->irq = irq;
err = request_irq(irq, me1400_ext_irq_isr,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME1400_NAME, (void *)subdevice);
if (err) {
PERROR("Can't get irq.\n");
me_subdevice_deinit(&subdevice->base);
kfree(subdevice);
return NULL;
}
PINFO("Registered irq=%d.\n", subdevice->irq);
/* Initialize registers */
subdevice->plx_intcs_reg = plx_reg_base + PLX_INTCSR_REG;
subdevice->ctrl_reg = me1400_reg_base + ME1400AB_EXT_IRQ_CTRL_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = me1400_reg_base;
#endif
// Enable IRQ on PLX
tmp =
inb(subdevice->
plx_intcs_reg) | (PLX_LOCAL_INT1_EN | PLX_LOCAL_INT1_POL |
PLX_PCI_INT_EN);
outb(tmp, subdevice->plx_intcs_reg);
PDEBUG_REG("ctrl_reg outb(Pplx:0x%lX)=0x%x\n", subdevice->plx_intcs_reg,
tmp);
/* Initialize the subdevice methods */
subdevice->base.me_subdevice_io_irq_start = me1400_ext_irq_io_irq_start;
subdevice->base.me_subdevice_io_irq_wait = me1400_ext_irq_io_irq_wait;
subdevice->base.me_subdevice_io_irq_stop = me1400_ext_irq_io_irq_stop;
subdevice->base.me_subdevice_io_reset_subdevice =
me1400_ext_irq_io_reset_subdevice;
subdevice->base.me_subdevice_query_number_channels =
me1400_ext_irq_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me1400_ext_irq_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me1400_ext_irq_query_subdevice_caps;
subdevice->base.me_subdevice_query_subdevice_caps_args =
me1400_ext_irq_query_subdevice_caps_args;
subdevice->base.me_subdevice_destructor = me1400_ext_irq_destructor;
subdevice->rised = 0;
subdevice->n = 0;
return subdevice;
}

View File

@ -0,0 +1,62 @@
/**
* @file me1400_ext_irq.h
*
* @brief ME-1400 external interrupt implementation.
* @note Copyright (C) 2006 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
#ifndef _ME1400_EXT_IRQ_H_
#define _ME1400_EXT_IRQ_H_
#include <linux/sched.h>
#include "mesubdevice.h"
#include "meslock.h"
#ifdef __KERNEL__
/**
* @brief The ME-1400 external interrupt subdevice class.
*/
typedef struct me1400_ext_irq_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
/* Attributes */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *clk_src_reg_lock; /**< Lock protecting the clock control register. */
wait_queue_head_t wait_queue; /**< Queue to put on threads waiting for an interrupt. */
uint32_t device_id; /**< The device id of the device holding the subdevice. */
int irq; /**< The irq number assigned by PCI BIOS. */
int rised; /**< If true an interrupt has occured. */
unsigned int n; /**< The number of interrupt since the driver was loaded. */
unsigned long plx_intcs_reg; /**< The PLX interrupt control and status register. */
unsigned long ctrl_reg; /**< The control register. */
#ifdef MEDEBUG_DEBUG_REG
unsigned long reg_base;
#endif
} me1400_ext_irq_subdevice_t;
/**
* @brief The constructor to generate a ME-1400 external interrupt instance.
*
* @param plx_reg_base The register base address of the PLX chip as returned by the PCI BIOS.
* @param me1400_reg_base The register base address of the ME-1400 device as returned by the PCI BIOS.
* @param irq The irq assigned by the PCI BIOS.
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me1400_ext_irq_subdevice_t *me1400_ext_irq_constructor(uint32_t device_id,
uint32_t plx_reg_base,
uint32_t me1400_reg_base,
spinlock_t *
clk_src_reg_lock,
int irq);
#endif
#endif

View File

@ -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_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,132 @@
/**
* @file me1600_ao.h
*
* @brief Meilhaus ME-1600 analog output subdevice class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME1600_AO_H_
#define _ME1600_AO_H_
# include <linux/version.h>
# include "mesubdevice.h"
# ifdef __KERNEL__
# define ME1600_MAX_RANGES 2 /**< Specifies the maximum number of ranges in me1600_ao_subdevice_t::u_ranges und me1600_ao_subdevice_t::i_ranges. */
/**
* @brief Defines a entry in the range table.
*/
typedef struct me1600_ao_range_entry {
int32_t min;
int32_t max;
} me1600_ao_range_entry_t;
typedef struct me1600_ao_timeout {
unsigned long start_time;
unsigned long delay;
} me1600_ao_timeout_t;
typedef struct me1600_ao_shadow {
int count;
unsigned long *registry;
uint16_t *shadow;
uint16_t *mirror;
uint16_t synchronous; /**< Synchronization list. */
uint16_t trigger; /**< Synchronization flag. */
} me1600_ao_shadow_t;
typedef enum ME1600_AO_STATUS {
ao_status_none = 0,
ao_status_single_configured,
ao_status_single_run,
ao_status_single_end,
ao_status_last
} ME1600_AO_STATUS;
/**
* @brief The ME-1600 analog output subdevice class.
*/
typedef struct me1600_ao_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
/* Attributes */
int ao_idx; /**< The index of the analog output subdevice on the device. */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *config_regs_lock; /**< Spin lock to protect configuration registers from concurrent access. */
int u_ranges_count; /**< The number of voltage ranges available on this subdevice. */
me1600_ao_range_entry_t u_ranges[ME1600_MAX_RANGES]; /**< Array holding the voltage ranges on this subdevice. */
int i_ranges_count; /**< The number of current ranges available on this subdevice. */
me1600_ao_range_entry_t i_ranges[ME1600_MAX_RANGES]; /**< Array holding the current ranges on this subdevice. */
/* Registers */
unsigned long uni_bi_reg; /**< Register for switching between unipoar and bipolar output mode. */
unsigned long i_range_reg; /**< Register for switching between ranges. */
unsigned long sim_output_reg; /**< Register used in order to update all channels simultaneously. */
unsigned long current_on_reg; /**< Register enabling current output on the fourth subdevice. */
# ifdef PDEBUG_REG
unsigned long reg_base;
# endif
ME1600_AO_STATUS status;
me1600_ao_shadow_t *ao_regs_shadows; /**< Addresses and shadows of output's registers. */
spinlock_t *ao_shadows_lock; /**< Protects the shadow's struct. */
int mode; /**< Mode in witch output should works. */
wait_queue_head_t wait_queue; /**< Wait queue to put on tasks waiting for data to arrive. */
me1600_ao_timeout_t timeout; /**< The timeout for start in blocking and non-blocking mode. */
struct workqueue_struct *me1600_workqueue;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
struct work_struct ao_control_task;
#else
struct delayed_work ao_control_task;
#endif
volatile int ao_control_task_flag; /**< Flag controling reexecuting of control task */
} me1600_ao_subdevice_t;
/**
* @brief The constructor to generate a subdevice template instance.
*
* @param reg_base The register base address of the device as returned by the PCI BIOS.
* @param ao_idx The index of the analog output subdevice on the device.
* @param current Flag indicating that analog output with #ao_idx of 3 is capable of current output.
* @param config_regs_lock Pointer to spin lock protecting the configuration registers and from concurrent access.
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me1600_ao_subdevice_t *me1600_ao_constructor(uint32_t reg_base,
unsigned int ao_idx,
int curr,
spinlock_t * config_regs_lock,
spinlock_t * ao_shadows_lock,
me1600_ao_shadow_t *
ao_regs_shadows,
struct workqueue_struct
*me1600_wq);
# endif //__KERNEL__
#endif //_ME1600_AO_H_

View File

@ -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

View File

@ -0,0 +1,261 @@
/**
* @file me1600_device.c
*
* @brief ME-1600 device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "medevice.h"
#include "mesubdevice.h"
#include "me1600_device.h"
static void me1600_set_registry(me1600_device_t * subdevice, uint32_t reg_base);
static void me1600_destructor(struct me_device *device);
/**
* @brief Global variable.
* This is working queue for runing a separate atask that will be responsible for work status (start, stop, timeouts).
*/
static struct workqueue_struct *me1600_workqueue;
me_device_t *me1600_pci_constructor(struct pci_dev *pci_device)
{
int err;
me1600_device_t *me1600_device;
me_subdevice_t *subdevice;
unsigned int chip_idx;
int i;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me1600_device = kmalloc(sizeof(me1600_device_t), GFP_KERNEL);
if (!me1600_device) {
PERROR("Cannot get memory for device instance.\n");
return NULL;
}
memset(me1600_device, 0, sizeof(me1600_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me1600_device, pci_device);
if (err) {
kfree(me1600_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
// Initialize spin lock .
spin_lock_init(&me1600_device->config_regs_lock);
spin_lock_init(&me1600_device->ao_shadows_lock);
// Get the number of analog output subdevices.
chip_idx =
me1600_versions_get_device_index(me1600_device->base.info.pci.
device_id);
// Create shadow instance.
me1600_device->ao_regs_shadows.count =
me1600_versions[chip_idx].ao_chips;
me1600_device->ao_regs_shadows.registry =
kmalloc(me1600_versions[chip_idx].ao_chips * sizeof(unsigned long),
GFP_KERNEL);
me1600_set_registry(me1600_device,
me1600_device->base.info.pci.reg_bases[2]);
me1600_device->ao_regs_shadows.shadow =
kmalloc(me1600_versions[chip_idx].ao_chips * sizeof(uint16_t),
GFP_KERNEL);
me1600_device->ao_regs_shadows.mirror =
kmalloc(me1600_versions[chip_idx].ao_chips * sizeof(uint16_t),
GFP_KERNEL);
// Create subdevice instances.
for (i = 0; i < me1600_versions[chip_idx].ao_chips; i++) {
subdevice =
(me_subdevice_t *) me1600_ao_constructor(me1600_device->
base.info.pci.
reg_bases[2], i,
((me1600_versions
[chip_idx].curr >
i) ? 1 : 0),
&me1600_device->
config_regs_lock,
&me1600_device->
ao_shadows_lock,
&me1600_device->
ao_regs_shadows,
me1600_workqueue);
if (!subdevice) {
me_device_deinit((me_device_t *) me1600_device);
kfree(me1600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me1600_device->base.slist,
subdevice);
}
// Overwrite base class methods.
me1600_device->base.me_device_destructor = me1600_destructor;
return (me_device_t *) me1600_device;
}
static void me1600_destructor(struct me_device *device)
{
me1600_device_t *me1600_device = (me1600_device_t *) device;
PDEBUG("executed.\n");
// Destroy shadow instance.
kfree(me1600_device->ao_regs_shadows.registry);
kfree(me1600_device->ao_regs_shadows.shadow);
kfree(me1600_device->ao_regs_shadows.mirror);
me_device_deinit((me_device_t *) me1600_device);
kfree(me1600_device);
}
static void me1600_set_registry(me1600_device_t * subdevice, uint32_t reg_base)
{ // Create shadow structure.
if (subdevice->ao_regs_shadows.count >= 1) {
subdevice->ao_regs_shadows.registry[0] =
(unsigned long)(reg_base + ME1600_CHANNEL_0_REG);
}
if (subdevice->ao_regs_shadows.count >= 2) {
subdevice->ao_regs_shadows.registry[1] =
(unsigned long)(reg_base + ME1600_CHANNEL_1_REG);
}
if (subdevice->ao_regs_shadows.count >= 3) {
subdevice->ao_regs_shadows.registry[2] =
(unsigned long)(reg_base + ME1600_CHANNEL_2_REG);
}
if (subdevice->ao_regs_shadows.count >= 4) {
subdevice->ao_regs_shadows.registry[3] =
(unsigned long)(reg_base + ME1600_CHANNEL_3_REG);
}
if (subdevice->ao_regs_shadows.count >= 5) {
subdevice->ao_regs_shadows.registry[4] =
(unsigned long)(reg_base + ME1600_CHANNEL_4_REG);
}
if (subdevice->ao_regs_shadows.count >= 6) {
subdevice->ao_regs_shadows.registry[5] =
(unsigned long)(reg_base + ME1600_CHANNEL_5_REG);
}
if (subdevice->ao_regs_shadows.count >= 7) {
subdevice->ao_regs_shadows.registry[6] =
(unsigned long)(reg_base + ME1600_CHANNEL_6_REG);
}
if (subdevice->ao_regs_shadows.count >= 8) {
subdevice->ao_regs_shadows.registry[7] =
(unsigned long)(reg_base + ME1600_CHANNEL_7_REG);
}
if (subdevice->ao_regs_shadows.count >= 9) {
subdevice->ao_regs_shadows.registry[8] =
(unsigned long)(reg_base + ME1600_CHANNEL_8_REG);
}
if (subdevice->ao_regs_shadows.count >= 10) {
subdevice->ao_regs_shadows.registry[9] =
(unsigned long)(reg_base + ME1600_CHANNEL_9_REG);
}
if (subdevice->ao_regs_shadows.count >= 11) {
subdevice->ao_regs_shadows.registry[10] =
(unsigned long)(reg_base + ME1600_CHANNEL_10_REG);
}
if (subdevice->ao_regs_shadows.count >= 12) {
subdevice->ao_regs_shadows.registry[11] =
(unsigned long)(reg_base + ME1600_CHANNEL_11_REG);
}
if (subdevice->ao_regs_shadows.count >= 13) {
subdevice->ao_regs_shadows.registry[12] =
(unsigned long)(reg_base + ME1600_CHANNEL_12_REG);
}
if (subdevice->ao_regs_shadows.count >= 14) {
subdevice->ao_regs_shadows.registry[13] =
(unsigned long)(reg_base + ME1600_CHANNEL_13_REG);
}
if (subdevice->ao_regs_shadows.count >= 15) {
subdevice->ao_regs_shadows.registry[14] =
(unsigned long)(reg_base + ME1600_CHANNEL_14_REG);
}
if (subdevice->ao_regs_shadows.count >= 16) {
subdevice->ao_regs_shadows.registry[15] =
(unsigned long)(reg_base + ME1600_CHANNEL_15_REG);
}
if (subdevice->ao_regs_shadows.count > 16) {
PERROR("More than 16 outputs! (%d)\n",
subdevice->ao_regs_shadows.count);
}
}
// Init and exit of module.
static int __init me1600_init(void)
{
PDEBUG("executed\n.");
me1600_workqueue = create_singlethread_workqueue("me1600");
return 0;
}
static void __exit me1600_exit(void)
{
PDEBUG("executed\n.");
flush_workqueue(me1600_workqueue);
destroy_workqueue(me1600_workqueue);
}
module_init(me1600_init);
module_exit(me1600_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for ME-1600 Device");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-1600 Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me1600_pci_constructor);

View File

@ -0,0 +1,101 @@
/**
* @file me1600_device.h
*
* @brief ME-1600 device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME1600_H
#define _ME1600_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#include "me1600_ao.h"
#include "me1600_ao_reg.h"
#ifdef __KERNEL__
/**
* @brief Structure to store device capabilities.
*/
typedef struct me1600_version {
uint16_t device_id; /**< The PCI device id of the device. */
unsigned int ao_chips; /**< The number of analog outputs on the device. */
int curr; /**< Flag to identify amounts of current output. */
} me1600_version_t;
/**
* @brief Defines for each ME-1600 device version its capabilities.
*/
static me1600_version_t me1600_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME1600_4U, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME1600_8U, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME1600_12U, 12, 0},
{PCI_DEVICE_ID_MEILHAUS_ME1600_16U, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME1600_16U_8I, 16, 8},
{0}
};
/**< Returns the number of entries in #me1600_versions. */
#define ME1600_DEVICE_VERSIONS (sizeof(me1600_versions) / sizeof(me1600_version_t) - 1)
/**
* @brief Returns the index of the device entry in #me1600_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me1600_versions.
*/
static inline unsigned int me1600_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME1600_DEVICE_VERSIONS; i++)
if (me1600_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-1600 device class structure.
*/
typedef struct me1600_device {
me_device_t base; /**< The Meilhaus device base class. */
spinlock_t config_regs_lock; /**< Protects the configuration registers. */
me1600_ao_shadow_t ao_regs_shadows; /**< Addresses and shadows of output's registers. */
spinlock_t ao_shadows_lock; /**< Protects the shadow's struct. */
} me1600_device_t;
/**
* @brief The ME-1600 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-1600 device instance. \n
* NULL on error.
*/
me_device_t *me1600_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
/**
* @file me4600_ai.h
*
* @brief Meilhaus ME-4000 analog input subdevice class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME4600_AI_H_
#define _ME4600_AI_H_
#include <linux/version.h>
#include "mesubdevice.h"
#include "meioctl.h"
#include "mecirc_buf.h"
#ifdef __KERNEL__
#define ME4600_AI_MAX_DATA 0xFFFF
#ifdef ME_SYNAPSE
# define ME4600_AI_CIRC_BUF_SIZE_ORDER 8 // 2^n PAGES =>> Maximum value of 1MB for Synapse
#else
# define ME4600_AI_CIRC_BUF_SIZE_ORDER 5 // 2^n PAGES =>> 128KB
#endif
#define ME4600_AI_CIRC_BUF_SIZE PAGE_SIZE<<ME4600_AI_CIRC_BUF_SIZE_ORDER // Buffer size in bytes.
#ifdef _CBUFF_32b_t
# define ME4600_AI_CIRC_BUF_COUNT ((ME4600_AI_CIRC_BUF_SIZE) / sizeof(uint32_t)) // Size in values
#else
# define ME4600_AI_CIRC_BUF_COUNT ((ME4600_AI_CIRC_BUF_SIZE) / sizeof(uint16_t)) // Size in values
#endif
#define ME4600_AI_FIFO_HALF 1024 //ME4600_AI_FIFO_COUNT/2 //1024
#define ME4600_AI_FIFO_MAX_SC 1352 //0.66*ME4600_AI_FIFO_COUNT //1352
typedef enum ME4600_AI_STATUS {
ai_status_none = 0,
ai_status_single_configured,
ai_status_stream_configured,
ai_status_stream_run_wait,
ai_status_stream_run,
ai_status_stream_end_wait,
ai_status_stream_end,
ai_status_stream_fifo_error,
ai_status_stream_buffer_error,
ai_status_stream_error,
ai_status_last
} ME4600_AI_STATUS;
typedef struct me4600_single_config_entry {
unsigned short status;
uint32_t entry;
uint32_t ctrl;
} me4600_single_config_entry_t;
typedef struct me4600_range_entry {
int min;
int max;
} me4600_range_entry_t;
typedef struct me4600_ai_ISM {
volatile unsigned int global_read; /**< The number of data read in total. */
volatile unsigned int read; /**< The number of data read for this chunck. */
volatile unsigned int next; /**< The number of data request by user. */
} me4600_ai_ISM_t;
typedef struct me4600_ai_timeout {
unsigned long start_time;
unsigned long delay;
} me4600_ai_timeout_t;
/**
* @brief The ME-4000 analog input subdevice class.
*/
typedef struct me4600_ai_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
/* Attributes */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *ctrl_reg_lock; /**< Spin lock to protect #ctrl_reg from concurrent access. */
/* Hardware feautres */
unsigned int irq; /**< The interrupt request number assigned by the PCI BIOS. */
int isolated; /**< Marks if this subdevice is on an optoisolated device. */
int sh; /**< Marks if this subdevice has sample and hold devices. */
unsigned int channels; /**< The number of channels available on this subdevice. */
me4600_single_config_entry_t single_config[32]; /**< The configuration set for single acquisition. */
unsigned int data_required; /**< The number of data request by user. */
unsigned int fifo_irq_threshold; /**< The user adjusted FIFO high water interrupt level. */
unsigned int chan_list_len; /**< The length of the user defined channel list. */
me4600_ai_ISM_t ISM; /**< The information request by Interrupt-State-Machine. */
volatile enum ME4600_AI_STATUS status; /**< The current stream status flag. */
me4600_ai_timeout_t timeout; /**< The timeout for start in blocking and non-blocking mode. */
/* Registers *//**< All registers are 32 bits long. */
unsigned long ctrl_reg;
unsigned long status_reg;
unsigned long channel_list_reg;
unsigned long data_reg;
unsigned long chan_timer_reg;
unsigned long chan_pre_timer_reg;
unsigned long scan_timer_low_reg;
unsigned long scan_timer_high_reg;
unsigned long scan_pre_timer_low_reg;
unsigned long scan_pre_timer_high_reg;
unsigned long start_reg;
unsigned long irq_status_reg;
unsigned long sample_counter_reg;
unsigned int ranges_len;
me4600_range_entry_t ranges[4]; /**< The ranges available on this subdevice. */
/* Software buffer */
me_circ_buf_t circ_buf; /**< Circular buffer holding measurment data. */
wait_queue_head_t wait_queue; /**< Wait queue to put on tasks waiting for data to arrive. */
struct workqueue_struct *me4600_workqueue;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
struct work_struct ai_control_task;
#else
struct delayed_work ai_control_task;
#endif
volatile int ai_control_task_flag; /**< Flag controling reexecuting of control task */
#ifdef MEDEBUG_DEBUG_REG
unsigned long reg_base;
#endif
} me4600_ai_subdevice_t;
/**
* @brief The constructor to generate a ME-4000 analog input subdevice instance.
*
* @param reg_base The register base address of the device as returned by the PCI BIOS.
* @param channels The number of analog input channels available on this subdevice.
* @param channels The number of analog input ranges available on this subdevice.
* @param isolated Flag indicating if this device is opto isolated.
* @param sh Flag indicating if sample and hold devices are available.
* @param irq The irq number assigned by PCI BIOS.
* @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me4600_ai_subdevice_t *me4600_ai_constructor(uint32_t reg_base,
unsigned int channels,
unsigned int ranges,
int isolated,
int sh,
int irq,
spinlock_t * ctrl_reg_lock,
struct workqueue_struct
*me4600_wq);
#endif
#endif

View File

@ -0,0 +1,107 @@
/**
* @file me4600_ai_reg.h
*
* @brief ME-4000 analog input subdevice register definitions.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME4600_AI_REG_H_
#define _ME4600_AI_REG_H_
#ifdef __KERNEL__
#define ME4600_AI_CTRL_REG 0x74 // _/W
#define ME4600_AI_STATUS_REG 0x74 // R/_
#define ME4600_AI_CHANNEL_LIST_REG 0x78 // _/W
#define ME4600_AI_DATA_REG 0x7C // R/_
#define ME4600_AI_CHAN_TIMER_REG 0x80 // _/W
#define ME4600_AI_CHAN_PRE_TIMER_REG 0x84 // _/W
#define ME4600_AI_SCAN_TIMER_LOW_REG 0x88 // _/W
#define ME4600_AI_SCAN_TIMER_HIGH_REG 0x8C // _/W
#define ME4600_AI_SCAN_PRE_TIMER_LOW_REG 0x90 // _/W
#define ME4600_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 // _/W
#define ME4600_AI_START_REG 0x98 // R/_
#define ME4600_AI_SAMPLE_COUNTER_REG 0xC0 // _/W
#define ME4600_AI_CTRL_BIT_MODE_0 0x00000001
#define ME4600_AI_CTRL_BIT_MODE_1 0x00000002
#define ME4600_AI_CTRL_BIT_MODE_2 0x00000004
#define ME4600_AI_CTRL_BIT_SAMPLE_HOLD 0x00000008
#define ME4600_AI_CTRL_BIT_IMMEDIATE_STOP 0x00000010
#define ME4600_AI_CTRL_BIT_STOP 0x00000020
#define ME4600_AI_CTRL_BIT_CHANNEL_FIFO 0x00000040
#define ME4600_AI_CTRL_BIT_DATA_FIFO 0x00000080
#define ME4600_AI_CTRL_BIT_FULLSCALE 0x00000100
#define ME4600_AI_CTRL_BIT_OFFSET 0x00000200
#define ME4600_AI_CTRL_BIT_EX_TRIG_ANALOG 0x00000400
#define ME4600_AI_CTRL_BIT_EX_TRIG 0x00000800
#define ME4600_AI_CTRL_BIT_EX_TRIG_FALLING 0x00001000
#define ME4600_AI_CTRL_BIT_EX_IRQ 0x00002000
#define ME4600_AI_CTRL_BIT_EX_IRQ_RESET 0x00004000
#define ME4600_AI_CTRL_BIT_LE_IRQ 0x00008000
#define ME4600_AI_CTRL_BIT_LE_IRQ_RESET 0x00010000
#define ME4600_AI_CTRL_BIT_HF_IRQ 0x00020000
#define ME4600_AI_CTRL_BIT_HF_IRQ_RESET 0x00040000
#define ME4600_AI_CTRL_BIT_SC_IRQ 0x00080000
#define ME4600_AI_CTRL_BIT_SC_IRQ_RESET 0x00100000
#define ME4600_AI_CTRL_BIT_SC_RELOAD 0x00200000
#define ME4600_AI_CTRL_BIT_EX_TRIG_BOTH 0x80000000
#define ME4600_AI_STATUS_BIT_EF_CHANNEL 0x00400000
#define ME4600_AI_STATUS_BIT_HF_CHANNEL 0x00800000
#define ME4600_AI_STATUS_BIT_FF_CHANNEL 0x01000000
#define ME4600_AI_STATUS_BIT_EF_DATA 0x02000000
#define ME4600_AI_STATUS_BIT_HF_DATA 0x04000000
#define ME4600_AI_STATUS_BIT_FF_DATA 0x08000000
#define ME4600_AI_STATUS_BIT_LE 0x10000000
#define ME4600_AI_STATUS_BIT_FSM 0x20000000
#define ME4600_AI_CTRL_RPCI_FIFO 0x40000000 //Always set to zero!
#define ME4600_AI_BASE_FREQUENCY 33E6
#define ME4600_AI_MIN_ACQ_TICKS 66LL
#define ME4600_AI_MAX_ACQ_TICKS 0xFFFFFFFFLL
#define ME4600_AI_MIN_SCAN_TICKS 66LL
#define ME4600_AI_MAX_SCAN_TICKS 0xFFFFFFFFFLL
#define ME4600_AI_MIN_CHAN_TICKS 66LL
#define ME4600_AI_MAX_CHAN_TICKS 0xFFFFFFFFLL
#define ME4600_AI_FIFO_COUNT 2048
#define ME4600_AI_LIST_COUNT 1024
#define ME4600_AI_LIST_INPUT_SINGLE_ENDED 0x000
#define ME4600_AI_LIST_INPUT_DIFFERENTIAL 0x020
#define ME4600_AI_LIST_RANGE_BIPOLAR_10 0x000
#define ME4600_AI_LIST_RANGE_BIPOLAR_2_5 0x040
#define ME4600_AI_LIST_RANGE_UNIPOLAR_10 0x080
#define ME4600_AI_LIST_RANGE_UNIPOLAR_2_5 0x0C0
#define ME4600_AI_LIST_LAST_ENTRY 0x100
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,263 @@
/**
* @file me4600_ao.h
*
* @brief Meilhaus ME-4000 analog output subdevice class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME4600_AO_H_
# define _ME4600_AO_H_
# include <linux/version.h>
# include "mesubdevice.h"
# include "mecirc_buf.h"
# include "meioctl.h"
# ifdef __KERNEL__
# ifdef BOSCH
# undef ME_SYNAPSE
# ifndef _CBUFF_32b_t
# define _CBUFF_32b_t
# endif //_CBUFF_32b_t
# endif //BOSCH
# define ME4600_AO_MAX_SUBDEVICES 4
# define ME4600_AO_FIFO_COUNT 4096
# define ME4600_AO_BASE_FREQUENCY 33000000LL
# define ME4600_AO_MIN_ACQ_TICKS 0LL
# define ME4600_AO_MAX_ACQ_TICKS 0LL
# define ME4600_AO_MIN_CHAN_TICKS 66LL
# define ME4600_AO_MAX_CHAN_TICKS 0xFFFFFFFFLL
# define ME4600_AO_MIN_RANGE -10000000
# define ME4600_AO_MAX_RANGE 9999694
# define ME4600_AO_MAX_DATA 0xFFFF
# ifdef ME_SYNAPSE
# define ME4600_AO_CIRC_BUF_SIZE_ORDER 8 // 2^n PAGES =>> Maximum value of 1MB for Synapse
# else
# define ME4600_AO_CIRC_BUF_SIZE_ORDER 5 // 2^n PAGES =>> 128KB
# endif
# define ME4600_AO_CIRC_BUF_SIZE PAGE_SIZE<<ME4600_AO_CIRC_BUF_SIZE_ORDER // Buffer size in bytes.
# ifdef _CBUFF_32b_t
# define ME4600_AO_CIRC_BUF_COUNT ((ME4600_AO_CIRC_BUF_SIZE) / sizeof(uint32_t)) // Size in values
# else
# define ME4600_AO_CIRC_BUF_COUNT ((ME4600_AO_CIRC_BUF_SIZE) / sizeof(uint16_t)) // Size in values
# endif
# define ME4600_AO_CONTINOUS 0x0
# define ME4600_AO_WRAP_MODE 0x1
# define ME4600_AO_HW_MODE 0x2
# define ME4600_AO_HW_WRAP_MODE (ME4600_AO_WRAP_MODE | ME4600_AO_HW_MODE)
# define ME4600_AO_SW_WRAP_MODE ME4600_AO_WRAP_MODE
# define ME4600_AO_INF_STOP_MODE 0x0
# define ME4600_AO_ACQ_STOP_MODE 0x1
# define ME4600_AO_SCAN_STOP_MODE 0x2
# ifdef BOSCH //SPECIAL BUILD FOR BOSCH
/* Bits for flags attribute. */
# define ME4600_AO_FLAGS_BROKEN_PIPE 0x1
# define ME4600_AO_FLAGS_SW_WRAP_MODE_0 0x2
# define ME4600_AO_FLAGS_SW_WRAP_MODE_1 0x4
# define ME4600_AO_FLAGS_SW_WRAP_MODE_MASK (ME4600_AO_FLAGS_SW_WRAP_MODE_0 | ME4600_AO_FLAGS_SW_WRAP_MODE_1)
# define ME4600_AO_FLAGS_SW_WRAP_MODE_NONE 0x0
# define ME4600_AO_FLAGS_SW_WRAP_MODE_INF 0x2
# define ME4600_AO_FLAGS_SW_WRAP_MODE_FIN 0x4
/**
* @brief The ME-4000 analog output subdevice class.
*/
typedef struct me4600_ao_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
/* Attributes */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *preload_reg_lock; /**< Spin lock to protect #preload_reg from concurrent access. */
uint32_t *preload_flags;
unsigned int irq; /**< The interrupt request number assigned by the PCI BIOS. */
me_circ_buf_t circ_buf; /**< Circular buffer holding measurment data. */
wait_queue_head_t wait_queue; /**< Wait queue to put on tasks waiting for data to arrive. */
int single_value; /**< Mirror of the value written in single mode. */
int volatile flags; /**< Flags used for storing SW wraparound setup and error signalling from ISR. */
unsigned int wrap_count; /**< The user defined wraparound cycle count. */
unsigned int wrap_remaining; /**< The wraparound cycle down counter used by a running conversion. */
unsigned int ao_idx; /**< The index of this analog output on this device. */
int fifo; /**< If set this device has a FIFO. */
int bosch_fw; /**< If set the bosch firmware is in PROM. */
/* Registers */
uint32_t ctrl_reg;
uint32_t status_reg;
uint32_t fifo_reg;
uint32_t single_reg;
uint32_t timer_reg;
uint32_t irq_status_reg;
uint32_t preload_reg;
uint32_t reg_base;
} me4600_ao_subdevice_t;
/**
* @brief The constructor to generate a ME-4000 analog output subdevice instance for BOSCH project.
*
* @param reg_base The register base address of the device as returned by the PCI BIOS.
* @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
* @param preload_flags Pointer to spin lock protecting the hold&trigger register from concurrent access.
* @param ao_idx Subdevice number.
* @param fifo Flag set if subdevice has hardware FIFO.
* @param irq IRQ number.
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
spinlock_t * preload_reg_lock,
uint32_t * preload_flags,
int ao_idx, int fifo, int irq);
# else //~BOSCH
//ME4600_AO_FLAGS_BROKEN_PIPE is OBSOLETE => Now problems are reported in status.
typedef enum ME4600_AO_STATUS {
ao_status_none = 0,
ao_status_single_configured,
ao_status_single_run_wait,
ao_status_single_run,
ao_status_single_end_wait,
ao_status_single_end,
ao_status_stream_configured,
ao_status_stream_run_wait,
ao_status_stream_run,
ao_status_stream_end_wait,
ao_status_stream_end,
ao_status_stream_fifo_error,
ao_status_stream_buffer_error,
ao_status_stream_error,
ao_status_last
} ME4600_AO_STATUS;
typedef struct me4600_ao_timeout {
unsigned long start_time;
unsigned long delay;
} me4600_ao_timeout_t;
/**
* @brief The ME-4600 analog output subdevice class.
*/
typedef struct me4600_ao_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
unsigned int ao_idx; /**< The index of this analog output on this device. */
/* Attributes */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *preload_reg_lock; /**< Spin lock to protect preload_reg from concurrent access. */
uint32_t *preload_flags;
/* Hardware feautres */
unsigned int irq; /**< The interrupt request number assigned by the PCI BIOS. */
int fifo; /**< If set this device has a FIFO. */
int bitpattern; /**< If set this device use bitpattern. */
int single_value; /**< Mirror of the output value in single mode. */
int single_value_in_fifo; /**< Mirror of the value written in single mode. */
uint32_t ctrl_trg; /**< Mirror of the trigger settings. */
volatile int mode; /**< Flags used for storing SW wraparound setup*/
int stop_mode; /**< The user defined stop condition flag. */
unsigned int start_mode;
unsigned int stop_count; /**< The user defined dates presentation end count. */
unsigned int stop_data_count; /**< The stop presentation count. */
unsigned int data_count; /**< The real presentation count. */
unsigned int preloaded_count; /**< The next data addres in buffer. <= for wraparound mode. */
int hardware_stop_delay; /**< The time that stop can take. This is only to not show hardware bug to user. */
volatile enum ME4600_AO_STATUS status; /**< The current stream status flag. */
me4600_ao_timeout_t timeout; /**< The timeout for start in blocking and non-blocking mode. */
/* Registers *//**< All registers are 32 bits long. */
unsigned long ctrl_reg;
unsigned long status_reg;
unsigned long fifo_reg;
unsigned long single_reg;
unsigned long timer_reg;
unsigned long irq_status_reg;
unsigned long preload_reg;
unsigned long reg_base;
/* Software buffer */
me_circ_buf_t circ_buf; /**< Circular buffer holding measurment data. 32 bit long */
wait_queue_head_t wait_queue; /**< Wait queue to put on tasks waiting for data to arrive. */
struct workqueue_struct *me4600_workqueue;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
struct work_struct ao_control_task;
#else
struct delayed_work ao_control_task;
#endif
volatile int ao_control_task_flag; /**< Flag controling reexecuting of control task */
} me4600_ao_subdevice_t;
/**
* @brief The constructor to generate a ME-4600 analog output subdevice instance.
*
* @param reg_base The register base address of the device as returned by the PCI BIOS.
* @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
* @param preload_flags Pointer to spin lock protecting the hold&trigger register from concurrent access.
* @param ao_idx Subdevice number.
* @param fifo Flag set if subdevice has hardware FIFO.
* @param irq IRQ number.
* @param me4600_wq Queue for asynchronous task (1 queue for all subdevice on 1 board).
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
spinlock_t * preload_reg_lock,
uint32_t * preload_flags,
int ao_idx,
int fifo,
int irq,
struct workqueue_struct
*me4600_wq);
# endif //BOSCH
# endif //__KERNEL__
#endif // ~_ME4600_AO_H_

View File

@ -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

View File

@ -0,0 +1,373 @@
/**
* @file me4600_device.c
*
* @brief ME-4600 device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "medevice.h"
#include "me4600_device.h"
#include "meplx_reg.h"
#include "mefirmware.h"
#include "mesubdevice.h"
#include "me4600_do.h"
#include "me4600_di.h"
#include "me4600_dio.h"
#include "me8254.h"
#include "me4600_ai.h"
#include "me4600_ao.h"
#include "me4600_ext_irq.h"
/**
* @brief Global variable.
* This is working queue for runing a separate atask that will be responsible for work status (start, stop, timeouts).
*/
static struct workqueue_struct *me4600_workqueue;
#ifdef BOSCH
me_device_t *me4600_pci_constructor(struct pci_dev *pci_device, int me_bosch_fw)
#else //~BOSCH
me_device_t *me4600_pci_constructor(struct pci_dev *pci_device)
#endif //BOSCH
{
me4600_device_t *me4600_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
int err;
int i;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me4600_device = kmalloc(sizeof(me4600_device_t), GFP_KERNEL);
if (!me4600_device) {
PERROR("Cannot get memory for ME-4600 device instance.\n");
return NULL;
}
memset(me4600_device, 0, sizeof(me4600_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me4600_device, pci_device);
if (err) {
kfree(me4600_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
// Download the xilinx firmware.
if (me4600_device->base.info.pci.device_id == PCI_DEVICE_ID_MEILHAUS_ME4610) { //Jekyll <=> me4610
err =
me_xilinx_download(me4600_device->base.info.pci.
reg_bases[1],
me4600_device->base.info.pci.
reg_bases[5], &pci_device->dev,
"me4610.bin");
} else { // General me4600 firmware
#ifdef BOSCH
err =
me_xilinx_download(me4600_device->base.info.pci.
reg_bases[1],
me4600_device->base.info.pci.
reg_bases[5], &pci_device->dev,
(me_bosch_fw) ? "me4600_bosch.bin" :
"me4600.bin");
#else //~BOSCH
err =
me_xilinx_download(me4600_device->base.info.pci.
reg_bases[1],
me4600_device->base.info.pci.
reg_bases[5], &pci_device->dev,
"me4600.bin");
#endif
}
if (err) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot download firmware.\n");
return NULL;
}
// Get the index in the device version information table.
version_idx =
me4600_versions_get_device_index(me4600_device->base.info.pci.
device_id);
// Initialize spin locks.
spin_lock_init(&me4600_device->preload_reg_lock);
me4600_device->preload_flags = 0;
spin_lock_init(&me4600_device->dio_lock);
spin_lock_init(&me4600_device->ai_ctrl_lock);
spin_lock_init(&me4600_device->ctr_ctrl_reg_lock);
spin_lock_init(&me4600_device->ctr_clk_src_reg_lock);
// Create digital input instances.
for (i = 0; i < me4600_versions[version_idx].di_subdevices; i++) {
subdevice =
(me_subdevice_t *) me4600_di_constructor(me4600_device->
base.info.pci.
reg_bases[2],
&me4600_device->
dio_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
// Create digital output instances.
for (i = 0; i < me4600_versions[version_idx].do_subdevices; i++) {
subdevice =
(me_subdevice_t *) me4600_do_constructor(me4600_device->
base.info.pci.
reg_bases[2],
&me4600_device->
dio_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
// Create digital input/output instances.
for (i = 0; i < me4600_versions[version_idx].dio_subdevices; i++) {
subdevice =
(me_subdevice_t *) me4600_dio_constructor(me4600_device->
base.info.pci.
reg_bases[2],
me4600_versions
[version_idx].
do_subdevices +
me4600_versions
[version_idx].
di_subdevices + i,
&me4600_device->
dio_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
// Create analog input instances.
for (i = 0; i < me4600_versions[version_idx].ai_subdevices; i++) {
subdevice =
(me_subdevice_t *) me4600_ai_constructor(me4600_device->
base.info.pci.
reg_bases[2],
me4600_versions
[version_idx].
ai_channels,
me4600_versions
[version_idx].
ai_ranges,
me4600_versions
[version_idx].
ai_isolated,
me4600_versions
[version_idx].
ai_sh,
me4600_device->
base.irq,
&me4600_device->
ai_ctrl_lock,
me4600_workqueue);
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
// Create analog output instances.
for (i = 0; i < me4600_versions[version_idx].ao_subdevices; i++) {
#ifdef BOSCH
subdevice =
(me_subdevice_t *) me4600_ao_constructor(me4600_device->
base.info.pci.
reg_bases[2],
&me4600_device->
preload_reg_lock,
&me4600_device->
preload_flags, i,
me4600_versions
[version_idx].
ao_fifo,
me4600_device->
base.irq);
#else //~BOSCH
subdevice =
(me_subdevice_t *) me4600_ao_constructor(me4600_device->
base.info.pci.
reg_bases[2],
&me4600_device->
preload_reg_lock,
&me4600_device->
preload_flags, i,
me4600_versions
[version_idx].
ao_fifo,
me4600_device->
base.irq,
me4600_workqueue);
#endif
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
// Create counter instances.
for (i = 0; i < me4600_versions[version_idx].ctr_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8254_constructor(me4600_device->base.
info.pci.device_id,
me4600_device->base.
info.pci.reg_bases[3],
0, i,
&me4600_device->
ctr_ctrl_reg_lock,
&me4600_device->
ctr_clk_src_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
// Create external interrupt instances.
for (i = 0; i < me4600_versions[version_idx].ext_irq_subdevices; i++) {
subdevice =
(me_subdevice_t *)
me4600_ext_irq_constructor(me4600_device->base.info.pci.
reg_bases[2],
me4600_device->base.irq,
&me4600_device->ai_ctrl_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me4600_device);
kfree(me4600_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me4600_device->base.slist,
subdevice);
}
return (me_device_t *) me4600_device;
}
// Init and exit of module.
static int __init me4600_init(void)
{
PDEBUG("executed.\n");
#ifndef BOSCH
me4600_workqueue = create_singlethread_workqueue("me4600");
#endif
return 0;
}
static void __exit me4600_exit(void)
{
PDEBUG("executed.\n");
#ifndef BOSCH
flush_workqueue(me4600_workqueue);
destroy_workqueue(me4600_workqueue);
#endif
}
module_init(me4600_init);
module_exit(me4600_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for ME-46xx Devices");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-46xx Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me4600_pci_constructor);

View File

@ -0,0 +1,151 @@
/**
* @file me4600_device.h
*
* @brief ME-4600 device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME4600_DEVICE_H
#define _ME4600_DEVICE_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
/**
* @brief Structure holding ME-4600 device capabilities.
*/
typedef struct me4600_version {
uint16_t device_id;
unsigned int do_subdevices;
unsigned int di_subdevices;
unsigned int dio_subdevices;
unsigned int ctr_subdevices;
unsigned int ai_subdevices;
unsigned int ai_channels;
unsigned int ai_ranges;
unsigned int ai_isolated;
unsigned int ai_sh;
unsigned int ao_subdevices;
unsigned int ao_fifo; //How many devices have FIFO
unsigned int ext_irq_subdevices;
} me4600_version_t;
/**
* @brief ME-4600 device capabilities.
*/
static me4600_version_t me4600_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME4610, 0, 0, 4, 3, 1, 16, 1, 0, 0, 0, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4650, 0, 0, 4, 0, 1, 16, 4, 0, 0, 0, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4660, 0, 0, 4, 3, 1, 16, 4, 0, 0, 2, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4660I, 1, 1, 2, 3, 1, 16, 4, 1, 0, 2, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4660S, 0, 0, 4, 3, 1, 16, 4, 0, 1, 2, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4660IS, 1, 1, 2, 3, 1, 16, 4, 1, 1, 2, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4670, 0, 0, 4, 3, 1, 32, 4, 0, 0, 4, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4670I, 1, 1, 2, 3, 1, 32, 4, 1, 0, 4, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4670S, 0, 0, 4, 3, 1, 32, 4, 0, 1, 4, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4670IS, 1, 1, 2, 3, 1, 32, 4, 1, 1, 4, 0, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4680, 0, 0, 4, 3, 1, 32, 4, 0, 0, 4, 4, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4680I, 1, 1, 2, 3, 1, 32, 4, 1, 0, 4, 4, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4680S, 0, 0, 4, 3, 1, 32, 4, 0, 1, 4, 4, 1},
{PCI_DEVICE_ID_MEILHAUS_ME4680IS, 1, 1, 2, 3, 1, 32, 4, 1, 1, 4, 4, 1},
{0},
};
#define ME4600_DEVICE_VERSIONS (sizeof(me4600_versions) / sizeof(me4600_version_t) - 1) /**< Returns the number of entries in #me4600_versions. */
/**
* @brief Returns the index of the device entry in #me4600_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me4600_versions.
*/
static inline unsigned int me4600_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME4600_DEVICE_VERSIONS; i++)
if (me4600_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-4600 device class structure.
*/
typedef struct me4600_device {
me_device_t base; /**< The Meilhaus device base class. */
/* Child class attributes. */
spinlock_t preload_reg_lock; /**< Guards the preload register of the anaolog output devices. */
unsigned int preload_flags; /**< Used in conjunction with #preload_reg_lock. */
spinlock_t dio_lock; /**< Locks the control register of the digital input/output subdevices. */
spinlock_t ai_ctrl_lock; /**< Locks the control register of the analog input subdevice. */
spinlock_t ctr_ctrl_reg_lock; /**< Locks the counter control register. */
spinlock_t ctr_clk_src_reg_lock; /**< Not used on this device but needed for the me8254 subdevice constructor call. */
} me4600_device_t;
/**
* @brief The ME-4600 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
* @param me_bosch_fw If set the device shall use the bosch firmware. (Only for special BOSCH build)
*
* @return On succes a new ME-4600 device instance. \n
* NULL on error.
*/
#ifdef BOSCH
/**
* @brief The ME-4600 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
* @param me_bosch_fw If set the device shall use the bosch firmware.
*
* @return On succes a new ME-4600 device instance. \n
* NULL on error.
*/
me_device_t *me4600_pci_constructor(struct pci_dev *pci_device, int me_bosch_fw)
__attribute__ ((weak));
#else //~BOSCH
/**
* @brief The ME-4600 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-4600 device instance. \n
* NULL on error.
*/
me_device_t *me4600_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif
#endif

View File

@ -0,0 +1,256 @@
/**
* @file me4600_di.c
*
* @brief ME-4000 digital input subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me4600_dio_reg.h"
#include "me4600_di.h"
/*
* Defines
*/
/*
* Functions
*/
static int me4600_di_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me4600_di_subdevice_t *instance;
uint32_t mode;
PDEBUG("executed.\n");
instance = (me4600_di_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inl(instance->ctrl_reg);
mode &= ~(ME4600_DIO_CTRL_BIT_MODE_2 | ME4600_DIO_CTRL_BIT_MODE_3); //0xFFF3
outl(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
spin_unlock(instance->ctrl_reg_lock);
outl(0, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me4600_di_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
int err = ME_ERRNO_SUCCESS;
me4600_di_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me4600_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
} else {
PERROR("Invalid port direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_di_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me4600_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me4600_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inl(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inl(instance->port_reg) & 0xFF;
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_di_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me4600_di_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DI;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me4600_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me4600_di_subdevice_t *me4600_di_constructor(uint32_t reg_base,
spinlock_t * ctrl_reg_lock)
{
me4600_di_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me4600_di_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me4600_di_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save the subdevice index */
subdevice->port_reg = reg_base + ME4600_DIO_PORT_1_REG;
subdevice->ctrl_reg = reg_base + ME4600_DIO_CTRL_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me4600_di_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me4600_di_io_single_config;
subdevice->base.me_subdevice_io_single_read = me4600_di_io_single_read;
subdevice->base.me_subdevice_query_number_channels =
me4600_di_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me4600_di_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me4600_di_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -0,0 +1,510 @@
/**
* @file me4600_dio.c
*
* @brief ME-4000 digital input/output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me4600_dio_reg.h"
#include "me4600_dio.h"
/*
* Defines
*/
/*
* Functions
*/
static int me4600_dio_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me4600_dio_subdevice_t *instance;
uint32_t mode;
PDEBUG("executed.\n");
instance = (me4600_dio_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
/* Set port to input mode */
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inl(instance->ctrl_reg);
mode &=
~((ME4600_DIO_CTRL_BIT_MODE_0 | ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
outl(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
spin_unlock(instance->ctrl_reg_lock);
outl(0, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me4600_dio_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me4600_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t mode;
uint32_t size =
flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
| ME_IO_SINGLE_CONFIG_DIO_WORD |
ME_IO_SINGLE_CONFIG_DIO_DWORD);
uint32_t mask;
PDEBUG("executed.\n");
instance = (me4600_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inl(instance->ctrl_reg);
switch (size) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
mode &=
~((ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
} else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
mode &=
~((ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
mode |=
ME4600_DIO_CTRL_BIT_MODE_0 << (instance->
dio_idx * 2);
} else if (single_config == ME_SINGLE_CONFIG_DIO_MUX32M) {
mask =
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) << (instance->
dio_idx *
2);
mask |=
ME4600_DIO_CTRL_BIT_FUNCTION_0 |
ME4600_DIO_CTRL_BIT_FUNCTION_1;
mask |=
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
instance->dio_idx;
mode &= ~mask;
if (ref == ME_REF_DIO_FIFO_LOW) {
mode |=
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2);
mode |= ME4600_DIO_CTRL_BIT_FUNCTION_1;
} else if (ref == ME_REF_DIO_FIFO_HIGH) {
mode |=
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2);
mode |= ME4600_DIO_CTRL_BIT_FUNCTION_1;
mode |=
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
instance->dio_idx;
} else {
PERROR
("Invalid port reference specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else if (single_config ==
ME_SINGLE_CONFIG_DIO_DEMUX32) {
mask =
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) << (instance->
dio_idx *
2);
mask |=
ME4600_DIO_CTRL_BIT_FUNCTION_0 |
ME4600_DIO_CTRL_BIT_FUNCTION_1;
mask |=
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
instance->dio_idx;
mode &= ~mask;
if (ref == ME_REF_DIO_FIFO_LOW) {
mode |=
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2);
mode |= ME4600_DIO_CTRL_BIT_FUNCTION_0;
} else if (ref == ME_REF_DIO_FIFO_HIGH) {
mode |=
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2);
mode |= ME4600_DIO_CTRL_BIT_FUNCTION_0;
mode |=
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
instance->dio_idx;
} else {
PERROR
("Invalid port reference specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else if (single_config ==
ME_SINGLE_CONFIG_DIO_BIT_PATTERN) {
mask =
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) << (instance->
dio_idx *
2);
mask |=
ME4600_DIO_CTRL_BIT_FUNCTION_0 |
ME4600_DIO_CTRL_BIT_FUNCTION_1;
mask |=
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
instance->dio_idx;
mode &= ~mask;
if (ref == ME_REF_DIO_FIFO_LOW) {
mode |=
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2);
} else if (ref == ME_REF_DIO_FIFO_HIGH) {
mode |=
(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2);
mode |=
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0 <<
instance->dio_idx;
} else {
PERROR
("Invalid port reference specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR
("Invalid port configuration specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outl(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_dio_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me4600_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t mode;
PDEBUG("executed.\n");
instance = (me4600_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inl(instance->
ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if ((mode ==
(ME4600_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) || !mode) {
*value =
inl(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inl(instance->
ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if ((mode ==
(ME4600_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) || !mode) {
*value = inl(instance->port_reg) & 0xFF;
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_dio_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me4600_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t mode;
uint32_t byte;
PDEBUG("executed.\n");
instance = (me4600_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inl(instance->
ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if (mode ==
(ME4600_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) {
byte = inl(instance->port_reg) & 0xFF;
if (value)
byte |= 0x1 << channel;
else
byte &= ~(0x1 << channel);
outl(byte, instance->port_reg);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inl(instance->
ctrl_reg) & ((ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if (mode ==
(ME4600_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) {
outl(value, instance->port_reg);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_dio_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me4600_dio_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DIO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me4600_dio_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_DIR_BYTE;
return ME_ERRNO_SUCCESS;
}
me4600_dio_subdevice_t *me4600_dio_constructor(uint32_t reg_base,
unsigned int dio_idx,
spinlock_t * ctrl_reg_lock)
{
me4600_dio_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me4600_dio_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me4600_dio_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save digital i/o index */
subdevice->dio_idx = dio_idx;
/* Save the subdevice index */
subdevice->ctrl_reg = reg_base + ME4600_DIO_CTRL_REG;
subdevice->port_reg = reg_base + ME4600_DIO_PORT_REG + (dio_idx * 4);
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me4600_dio_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me4600_dio_io_single_config;
subdevice->base.me_subdevice_io_single_read = me4600_dio_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me4600_dio_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me4600_dio_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me4600_dio_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me4600_dio_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,433 @@
/**
* @file me4600_do.c
*
* @brief ME-4000 digital output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me4600_dio_reg.h"
#include "me4600_do.h"
/*
* Defines
*/
/*
* Functions
*/
static int me4600_do_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me4600_do_subdevice_t *instance;
uint32_t mode;
PDEBUG("executed.\n");
instance = (me4600_do_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
/* Set port to output mode */
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inl(instance->ctrl_reg);
mode &= ~ME4600_DIO_CTRL_BIT_MODE_1; //0xFFFD
mode |= ME4600_DIO_CTRL_BIT_MODE_0; //0x1
outl(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
spin_unlock(instance->ctrl_reg_lock);
outl(0, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me4600_do_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me4600_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t mode;
int size =
flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
| ME_IO_SINGLE_CONFIG_DIO_WORD |
ME_IO_SINGLE_CONFIG_DIO_DWORD);
PDEBUG("executed.\n");
instance = (me4600_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inl(instance->ctrl_reg);
switch (size) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
mode &= ~(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1);
mode |= (ME4600_DIO_CTRL_BIT_MODE_0);
} else if (single_config == ME_SINGLE_CONFIG_DIO_MUX32M) {
mode &= ~(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_0 |
ME4600_DIO_CTRL_BIT_FUNCTION_1 |
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
if (ref == ME_REF_DIO_FIFO_LOW) {
mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_1);
} else if (ref == ME_REF_DIO_FIFO_HIGH) {
mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_1
|
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
} else {
PERROR
("Invalid port reference specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else if (single_config ==
ME_SINGLE_CONFIG_DIO_DEMUX32) {
mode &=
~(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_0 |
ME4600_DIO_CTRL_BIT_FUNCTION_1 |
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
if (ref == ME_REF_DIO_FIFO_LOW) {
mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_0);
} else if (ref == ME_REF_DIO_FIFO_HIGH) {
mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_0
|
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
} else {
PERROR
("Invalid port reference specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else if (single_config ==
ME_SINGLE_CONFIG_DIO_BIT_PATTERN) {
mode &=
~(ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FUNCTION_0 |
ME4600_DIO_CTRL_BIT_FUNCTION_1 |
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
if (ref == ME_REF_DIO_FIFO_LOW) {
mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1);
} else if (ref == ME_REF_DIO_FIFO_HIGH) {
mode |= (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1 |
ME4600_DIO_CTRL_BIT_FIFO_HIGH_0);
} else {
PERROR
("Invalid port reference specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid port direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outl(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_do_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me4600_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t mode;
PDEBUG("executed.\n");
instance = (me4600_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode =
inl(instance->
ctrl_reg) & (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1);
if (mode == ME4600_DIO_CTRL_BIT_MODE_0) {
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value =
inl(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inl(instance->port_reg) & 0xFF;
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
} else {
PERROR("Port not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_do_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me4600_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t byte;
uint32_t mode;
PDEBUG("executed.\n");
instance = (me4600_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode =
inl(instance->
ctrl_reg) & (ME4600_DIO_CTRL_BIT_MODE_0 |
ME4600_DIO_CTRL_BIT_MODE_1);
if (mode == ME4600_DIO_CTRL_BIT_MODE_0) {
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
byte = inl(instance->port_reg) & 0xFF;
if (value)
byte |= 0x1 << channel;
else
byte &= ~(0x1 << channel);
outl(byte, instance->port_reg);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
outl(value, instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
} else {
PERROR("Port not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_do_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me4600_do_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me4600_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = 0;
return ME_ERRNO_SUCCESS;
}
me4600_do_subdevice_t *me4600_do_constructor(uint32_t reg_base,
spinlock_t * ctrl_reg_lock)
{
me4600_do_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me4600_do_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me4600_do_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save the subdevice index */
subdevice->ctrl_reg = reg_base + ME4600_DIO_CTRL_REG;
subdevice->port_reg = reg_base + ME4600_DIO_PORT_0_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me4600_do_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me4600_do_io_single_config;
subdevice->base.me_subdevice_io_single_read = me4600_do_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me4600_do_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me4600_do_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me4600_do_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me4600_do_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -0,0 +1,467 @@
/**
* @file me4600_ext_irq.c
*
* @brief ME-4000 external interrupt subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/version.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "meids.h"
#include "me4600_reg.h"
#include "me4600_ai_reg.h"
#include "me4600_ext_irq_reg.h"
#include "me4600_ext_irq.h"
/*
* Defines
*/
/*
* Functions
*/
static int me4600_ext_irq_io_irq_start(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags)
{
me4600_ext_irq_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long cpu_flags;
uint32_t tmp;
PDEBUG("executed.\n");
instance = (me4600_ext_irq_subdevice_t *) subdevice;
if (flags & ~ME_IO_IRQ_START_DIO_BIT) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if ((irq_edge != ME_IRQ_EDGE_RISING)
&& (irq_edge != ME_IRQ_EDGE_FALLING)
&& (irq_edge != ME_IRQ_EDGE_ANY)
) {
PERROR("Invalid irq edge specified.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
if (irq_source != ME_IRQ_SOURCE_DIO_LINE) {
PERROR("Invalid irq source specified.\n");
return ME_ERRNO_INVALID_IRQ_SOURCE;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
tmp = 0x0; //inl(instance->ext_irq_config_reg);
if (irq_edge == ME_IRQ_EDGE_RISING) {
//tmp &= ~ME4600_EXT_IRQ_CONFIG_MASK;
//tmp |= ME4600_EXT_IRQ_CONFIG_MASK_RISING;
} else if (irq_edge == ME_IRQ_EDGE_FALLING) {
//tmp &= ~ME4600_EXT_IRQ_CONFIG_MASK;
//tmp |= ME4600_EXT_IRQ_CONFIG_MASK_FALLING;
tmp = ME4600_EXT_IRQ_CONFIG_MASK_FALLING;
} else if (irq_edge == ME_IRQ_EDGE_ANY) {
//tmp &= ~ME4600_EXT_IRQ_CONFIG_MASK;
//tmp |= ME4600_EXT_IRQ_CONFIG_MASK_ANY;
tmp = ME4600_EXT_IRQ_CONFIG_MASK_ANY;
}
outl(tmp, instance->ext_irq_config_reg);
PDEBUG_REG("ext_irq_config_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ext_irq_config_reg - instance->reg_base, tmp);
spin_lock_irqsave(instance->ctrl_reg_lock, cpu_flags);
tmp = inl(instance->ctrl_reg);
tmp &= ~(ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
tmp |= ME4600_AI_CTRL_BIT_EX_IRQ;
outl(tmp, instance->ctrl_reg);
spin_unlock_irqrestore(instance->ctrl_reg_lock, cpu_flags);
instance->rised = 0;
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_ext_irq_io_irq_wait(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags)
{
me4600_ext_irq_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
long t = 0;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me4600_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (time_out < 0) {
PERROR("Invalid time_out specified.\n");
return ME_ERRNO_INVALID_TIMEOUT;
}
if (time_out) {
t = (time_out * HZ) / 1000;
if (t == 0)
t = 1;
}
ME_SUBDEVICE_ENTER;
if (instance->rised <= 0) {
instance->rised = 0;
if (time_out) {
t = wait_event_interruptible_timeout(instance->
wait_queue,
(instance->rised !=
0), t);
if (t == 0) {
PERROR
("Wait on external interrupt timed out.\n");
err = ME_ERRNO_TIMEOUT;
}
} else {
wait_event_interruptible(instance->wait_queue,
(instance->rised != 0));
}
if (instance->rised < 0) {
PERROR("Wait on interrupt aborted by user.\n");
err = ME_ERRNO_CANCELLED;
}
}
if (signal_pending(current)) {
PERROR("Wait on external interrupt aborted by signal.\n");
err = ME_ERRNO_SIGNAL;
}
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
instance->rised = 0;
*irq_count = instance->count;
*value = instance->value;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_ext_irq_io_irq_stop(me_subdevice_t * subdevice,
struct file *filep,
int channel, int flags)
{
me4600_ext_irq_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long cpu_flags;
uint32_t tmp;
PDEBUG("executed.\n");
instance = (me4600_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->ctrl_reg_lock);
tmp = inl(instance->ctrl_reg);
tmp &= ~(ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
outl(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_regv outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->ctrl_reg_lock);
instance->rised = -1;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return err;
}
static int me4600_ext_irq_io_reset_subdevice(me_subdevice_t * subdevice,
struct file *filep, int flags)
{
me4600_ext_irq_subdevice_t *instance;
unsigned long cpu_flags;
uint32_t tmp;
PDEBUG("executed.\n");
instance = (me4600_ext_irq_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->ctrl_reg_lock);
tmp = inl(instance->ctrl_reg);
tmp &= ~(ME4600_AI_CTRL_BIT_EX_IRQ | ME4600_AI_CTRL_BIT_EX_IRQ_RESET);
outl(tmp, instance->ctrl_reg);
PDEBUG_REG("ctrl_regv outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->ctrl_reg_lock);
instance->rised = -1;
instance->count = 0;
outl(ME4600_EXT_IRQ_CONFIG_MASK_ANY, instance->ext_irq_config_reg);
PDEBUG_REG("ext_irq_config_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ext_irq_config_reg - instance->reg_base,
ME4600_EXT_IRQ_CONFIG_MASK_ANY);
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static void me4600_ext_irq_destructor(struct me_subdevice *subdevice)
{
me4600_ext_irq_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me4600_ext_irq_subdevice_t *) subdevice;
me_subdevice_deinit(&instance->base);
free_irq(instance->irq, instance);
kfree(instance);
}
static int me4600_ext_irq_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 1;
return ME_ERRNO_SUCCESS;
}
static int me4600_ext_irq_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_EXT_IRQ;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me4600_ext_irq_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps =
ME_CAPS_EXT_IRQ_EDGE_RISING | ME_CAPS_EXT_IRQ_EDGE_FALLING |
ME_CAPS_EXT_IRQ_EDGE_ANY;
return ME_ERRNO_SUCCESS;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me4600_ext_irq_isr(int irq, void *dev_id)
#else
static irqreturn_t me4600_ext_irq_isr(int irq, void *dev_id,
struct pt_regs *regs)
#endif
{
me4600_ext_irq_subdevice_t *instance;
uint32_t ctrl;
uint32_t irq_status;
instance = (me4600_ext_irq_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
irq_status = inl(instance->irq_status_reg);
if (!(irq_status & ME4600_IRQ_STATUS_BIT_EX)) {
PINFO("%ld Shared interrupt. %s(): irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, irq_status);
return IRQ_NONE;
}
PDEBUG("executed.\n");
spin_lock(&instance->subdevice_lock);
instance->rised = 1;
instance->value = inl(instance->ext_irq_value_reg);
instance->count++;
spin_lock(instance->ctrl_reg_lock);
ctrl = inl(instance->ctrl_reg);
ctrl |= ME4600_AI_CTRL_BIT_EX_IRQ_RESET;
outl(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
ctrl &= ~ME4600_AI_CTRL_BIT_EX_IRQ_RESET;
outl(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return IRQ_HANDLED;
}
me4600_ext_irq_subdevice_t *me4600_ext_irq_constructor(uint32_t reg_base,
int irq,
spinlock_t *
ctrl_reg_lock)
{
me4600_ext_irq_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me4600_ext_irq_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me4600_ext_irq_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Initialize wait queue */
init_waitqueue_head(&subdevice->wait_queue);
/* Register interrupt */
subdevice->irq = irq;
if (request_irq(subdevice->irq, me4600_ext_irq_isr,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME4600_NAME, subdevice)) {
PERROR("Cannot register interrupt.\n");
kfree(subdevice);
return NULL;
}
PINFO("Registered irq=%d.\n", subdevice->irq);
/* Initialize registers */
subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
subdevice->ctrl_reg = reg_base + ME4600_AI_CTRL_REG;
subdevice->ext_irq_config_reg = reg_base + ME4600_EXT_IRQ_CONFIG_REG;
subdevice->ext_irq_value_reg = reg_base + ME4600_EXT_IRQ_VALUE_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Override base class methods. */
subdevice->base.me_subdevice_destructor = me4600_ext_irq_destructor;
subdevice->base.me_subdevice_io_reset_subdevice =
me4600_ext_irq_io_reset_subdevice;
subdevice->base.me_subdevice_io_irq_start = me4600_ext_irq_io_irq_start;
subdevice->base.me_subdevice_io_irq_wait = me4600_ext_irq_io_irq_wait;
subdevice->base.me_subdevice_io_irq_stop = me4600_ext_irq_io_irq_stop;
subdevice->base.me_subdevice_query_number_channels =
me4600_ext_irq_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me4600_ext_irq_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me4600_ext_irq_query_subdevice_caps;
subdevice->rised = 0;
subdevice->count = 0;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,200 @@
/**
* @file me6000_ao.h
*
* @brief Meilhaus ME-6000 analog output subdevice class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME6000_AO_H_
#define _ME6000_AO_H_
#include <linux/version.h>
#include "mesubdevice.h"
#include "mecirc_buf.h"
#include "meioctl.h"
#ifdef __KERNEL__
#define ME6000_AO_MAX_SUBDEVICES 16
#define ME6000_AO_FIFO_COUNT 8192
#define ME6000_AO_BASE_FREQUENCY 33000000L
#define ME6000_AO_MIN_ACQ_TICKS 0LL
#define ME6000_AO_MAX_ACQ_TICKS 0LL
#define ME6000_AO_MIN_CHAN_TICKS 66LL
#define ME6000_AO_MAX_CHAN_TICKS 0xFFFFFFFFLL
#define ME6000_AO_MIN_RANGE -10000000
#define ME6000_AO_MAX_RANGE 9999694
#define ME6000_AO_MIN_RANGE_HIGH 0
#define ME6000_AO_MAX_RANGE_HIGH 49999237
#define ME6000_AO_MAX_DATA 0xFFFF
#ifdef ME_SYNAPSE
# define ME6000_AO_CIRC_BUF_SIZE_ORDER 8 // 2^n PAGES =>> Maximum value of 1MB for Synapse
#else
# define ME6000_AO_CIRC_BUF_SIZE_ORDER 5 // 2^n PAGES =>> 128KB
#endif
#define ME6000_AO_CIRC_BUF_SIZE PAGE_SIZE<<ME6000_AO_CIRC_BUF_SIZE_ORDER // Buffer size in bytes.
# ifdef _CBUFF_32b_t
# define ME6000_AO_CIRC_BUF_COUNT ((ME6000_AO_CIRC_BUF_SIZE) / sizeof(uint32_t)) // Size in values
# else
# define ME6000_AO_CIRC_BUF_COUNT ((ME6000_AO_CIRC_BUF_SIZE) / sizeof(uint16_t)) // Size in values
# endif
# define ME6000_AO_CONTINOUS 0x0
# define ME6000_AO_WRAP_MODE 0x1
# define ME6000_AO_HW_MODE 0x2
# define ME6000_AO_HW_WRAP_MODE (ME6000_AO_WRAP_MODE | ME6000_AO_HW_MODE)
# define ME6000_AO_SW_WRAP_MODE ME6000_AO_WRAP_MODE
# define ME6000_AO_INF_STOP_MODE 0x0
# define ME6000_AO_ACQ_STOP_MODE 0x1
# define ME6000_AO_SCAN_STOP_MODE 0x2
# define ME6000_AO_EXTRA_HARDWARE 0x1
# define ME6000_AO_HAS_FIFO 0x2
typedef enum ME6000_AO_STATUS {
ao_status_none = 0,
ao_status_single_configured,
ao_status_single_run_wait,
ao_status_single_run,
ao_status_single_end_wait,
ao_status_single_end,
ao_status_stream_configured,
ao_status_stream_run_wait,
ao_status_stream_run,
ao_status_stream_end_wait,
ao_status_stream_end,
ao_status_stream_fifo_error,
ao_status_stream_buffer_error,
ao_status_stream_error,
ao_status_last
} ME6000_AO_STATUS;
typedef struct me6000_ao_timeout {
unsigned long start_time;
unsigned long delay;
} me6000_ao_timeout_t;
/**
* @brief The ME-6000 analog output subdevice class.
*/
typedef struct me6000_ao_subdevice {
/* Inheritance */
me_subdevice_t base; /**< The subdevice base class. */
unsigned int ao_idx;
/* Attributes */
spinlock_t subdevice_lock; /**< Spin lock to protect the subdevice from concurrent access. */
spinlock_t *preload_reg_lock; /**< Spin lock to protect preload_reg from concurrent access. */
uint32_t *preload_flags;
uint32_t *triggering_flags;
/* Hardware feautres */
unsigned int irq; /**< The interrupt request number assigned by the PCI BIOS. */
int fifo; /**< If set this device has a FIFO. */
//Range
int min;
int max;
int single_value; /**< Mirror of the output value in single mode. */
int single_value_in_fifo; /**< Mirror of the value written in single mode. */
uint32_t ctrl_trg; /**< Mirror of the trigger settings. */
volatile int mode; /**< Flags used for storing SW wraparound setup*/
int stop_mode; /**< The user defined stop condition flag. */
unsigned int start_mode;
unsigned int stop_count; /**< The user defined dates presentation end count. */
unsigned int stop_data_count; /**< The stop presentation count. */
unsigned int data_count; /**< The real presentation count. */
unsigned int preloaded_count; /**< The next data addres in buffer. <= for wraparound mode. */
int hardware_stop_delay; /**< The time that stop can take. This is only to not show hardware bug to user. */
volatile enum ME6000_AO_STATUS status; /**< The current stream status flag. */
me6000_ao_timeout_t timeout; /**< The timeout for start in blocking and non-blocking mode. */
/* Registers *//**< All registers are 32 bits long. */
unsigned long ctrl_reg;
unsigned long status_reg;
unsigned long fifo_reg;
unsigned long single_reg;
unsigned long timer_reg;
unsigned long irq_status_reg;
unsigned long preload_reg;
unsigned long irq_reset_reg;
#ifdef MEDEBUG_DEBUG_REG
unsigned long reg_base;
#endif
/* Software buffer */
me_circ_buf_t circ_buf; /**< Circular buffer holding measurment data. */
wait_queue_head_t wait_queue; /**< Wait queue to put on tasks waiting for data to arrive. */
struct workqueue_struct *me6000_workqueue;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
struct work_struct ao_control_task;
#else
struct delayed_work ao_control_task;
#endif
volatile int ao_control_task_flag; /**< Flag controling reexecuting of control task */
} me6000_ao_subdevice_t;
/**
* @brief The constructor to generate a ME-6000 analog output subdevice instance.
*
* @param reg_base The register base address of the device as returned by the PCI BIOS.
* @param ctrl_reg_lock Pointer to spin lock protecting the control register from concurrent access.
* @param preload_flags Pointer to spin lock protecting the hold&trigger register from concurrent access.
* @param ao_idx Subdevice number.
* @param fifo Flag set if subdevice has hardware FIFO.
* @param irq IRQ number.
* @param high_range Flag set if subdevice has high curren output.
* @param me6000_wq Queue for asynchronous task (1 queue for all subdevice on 1 board).
*
* @return Pointer to new instance on success.\n
* NULL on error.
*/
me6000_ao_subdevice_t *me6000_ao_constructor(uint32_t reg_base,
spinlock_t * preload_reg_lock,
uint32_t * preload_flags,
uint32_t * triggering_flags,
int ao_idx,
int fifo,
int irq,
int high_range,
struct workqueue_struct
*me6000_wq);
#endif //__KERNEL__
#endif //_ME6000_AO_H_

View File

@ -0,0 +1,177 @@
/**
* @file me6000_ao_reg.h
*
* @brief ME-6000 analog output subdevice register definitions.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME6000_AO_REG_H_
#define _ME6000_AO_REG_H_
#ifdef __KERNEL__
// AO
#define ME6000_AO_00_CTRL_REG 0x00 // R/W
#define ME6000_AO_00_STATUS_REG 0x04 // R/_
#define ME6000_AO_00_FIFO_REG 0x08 // _/W
#define ME6000_AO_00_SINGLE_REG 0x0C // R/W
#define ME6000_AO_00_TIMER_REG 0x10 // _/W
#define ME6000_AO_01_CTRL_REG 0x18 // R/W
#define ME6000_AO_01_STATUS_REG 0x1C // R/_
#define ME6000_AO_01_FIFO_REG 0x20 // _/W
#define ME6000_AO_01_SINGLE_REG 0x24 // R/W
#define ME6000_AO_01_TIMER_REG 0x28 // _/W
#define ME6000_AO_02_CTRL_REG 0x30 // R/W
#define ME6000_AO_02_STATUS_REG 0x34 // R/_
#define ME6000_AO_02_FIFO_REG 0x38 // _/W
#define ME6000_AO_02_SINGLE_REG 0x3C // R/W
#define ME6000_AO_02_TIMER_REG 0x40 // _/W
#define ME6000_AO_03_CTRL_REG 0x48 // R/W
#define ME6000_AO_03_STATUS_REG 0x4C // R/_
#define ME6000_AO_03_FIFO_REG 0x50 // _/W
#define ME6000_AO_03_SINGLE_REG 0x54 // R/W
#define ME6000_AO_03_TIMER_REG 0x58 // _/W
#define ME6000_AO_SINGLE_STATUS_REG 0xA4 // R/_
#define ME6000_AO_SINGLE_STATUS_OFFSET 4 //The first single subdevice => bit 0 in ME6000_AO_SINGLE_STATUS_REG.
#define ME6000_AO_04_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_04_SINGLE_REG 0x74 // _/W
#define ME6000_AO_05_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_05_SINGLE_REG 0x78 // _/W
#define ME6000_AO_06_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_06_SINGLE_REG 0x7C // _/W
#define ME6000_AO_07_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_07_SINGLE_REG 0x80 // _/W
#define ME6000_AO_08_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_08_SINGLE_REG 0x84 // _/W
#define ME6000_AO_09_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_09_SINGLE_REG 0x88 // _/W
#define ME6000_AO_10_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_10_SINGLE_REG 0x8C // _/W
#define ME6000_AO_11_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_11_SINGLE_REG 0x90 // _/W
#define ME6000_AO_12_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_12_SINGLE_REG 0x94 // _/W
#define ME6000_AO_13_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_13_SINGLE_REG 0x98 // _/W
#define ME6000_AO_14_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_14_SINGLE_REG 0x9C // _/W
#define ME6000_AO_15_STATUS_REG ME6000_AO_SINGLE_STATUS_REG
#define ME6000_AO_15_SINGLE_REG 0xA0 // _/W
//ME6000_AO_CTRL_REG
#define ME6000_AO_MODE_SINGLE 0x00
#define ME6000_AO_MODE_WRAPAROUND 0x01
#define ME6000_AO_MODE_CONTINUOUS 0x02
#define ME6000_AO_CTRL_MODE_MASK (ME6000_AO_MODE_WRAPAROUND | ME6000_AO_MODE_CONTINUOUS)
#define ME6000_AO_CTRL_BIT_MODE_WRAPAROUND 0x001
#define ME6000_AO_CTRL_BIT_MODE_CONTINUOUS 0x002
#define ME6000_AO_CTRL_BIT_STOP 0x004
#define ME6000_AO_CTRL_BIT_ENABLE_FIFO 0x008
#define ME6000_AO_CTRL_BIT_ENABLE_EX_TRIG 0x010
#define ME6000_AO_CTRL_BIT_EX_TRIG_EDGE 0x020
#define ME6000_AO_CTRL_BIT_ENABLE_IRQ 0x040
#define ME6000_AO_CTRL_BIT_IMMEDIATE_STOP 0x080
#define ME6000_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH 0x800
//ME6000_AO_STATUS_REG
#define ME6000_AO_STATUS_BIT_FSM 0x01
#define ME6000_AO_STATUS_BIT_FF 0x02
#define ME6000_AO_STATUS_BIT_HF 0x04
#define ME6000_AO_STATUS_BIT_EF 0x08
#define ME6000_AO_PRELOAD_REG 0xA8 // R/W ///ME6000_AO_SYNC_REG <==> ME6000_AO_PRELOAD_REG
/*
#define ME6000_AO_SYNC_HOLD_0 0x00000001
#define ME6000_AO_SYNC_HOLD_1 0x00000002
#define ME6000_AO_SYNC_HOLD_2 0x00000004
#define ME6000_AO_SYNC_HOLD_3 0x00000008
#define ME6000_AO_SYNC_HOLD_4 0x00000010
#define ME6000_AO_SYNC_HOLD_5 0x00000020
#define ME6000_AO_SYNC_HOLD_6 0x00000040
#define ME6000_AO_SYNC_HOLD_7 0x00000080
#define ME6000_AO_SYNC_HOLD_8 0x00000100
#define ME6000_AO_SYNC_HOLD_9 0x00000200
#define ME6000_AO_SYNC_HOLD_10 0x00000400
#define ME6000_AO_SYNC_HOLD_11 0x00000800
#define ME6000_AO_SYNC_HOLD_12 0x00001000
#define ME6000_AO_SYNC_HOLD_13 0x00002000
#define ME6000_AO_SYNC_HOLD_14 0x00004000
#define ME6000_AO_SYNC_HOLD_15 0x00008000
*/
#define ME6000_AO_SYNC_HOLD 0x00000001
/*
#define ME6000_AO_SYNC_EXT_TRIG_0 0x00010000
#define ME6000_AO_SYNC_EXT_TRIG_1 0x00020000
#define ME6000_AO_SYNC_EXT_TRIG_2 0x00040000
#define ME6000_AO_SYNC_EXT_TRIG_3 0x00080000
#define ME6000_AO_SYNC_EXT_TRIG_4 0x00100000
#define ME6000_AO_SYNC_EXT_TRIG_5 0x00200000
#define ME6000_AO_SYNC_EXT_TRIG_6 0x00400000
#define ME6000_AO_SYNC_EXT_TRIG_7 0x00800000
#define ME6000_AO_SYNC_EXT_TRIG_8 0x01000000
#define ME6000_AO_SYNC_EXT_TRIG_9 0x02000000
#define ME6000_AO_SYNC_EXT_TRIG_10 0x04000000
#define ME6000_AO_SYNC_EXT_TRIG_11 0x08000000
#define ME6000_AO_SYNC_EXT_TRIG_12 0x10000000
#define ME6000_AO_SYNC_EXT_TRIG_13 0x20000000
#define ME6000_AO_SYNC_EXT_TRIG_14 0x40000000
#define ME6000_AO_SYNC_EXT_TRIG_15 0x80000000
*/
#define ME6000_AO_SYNC_EXT_TRIG 0x00010000
#define ME6000_AO_EXT_TRIG 0x80000000
// AO-IRQ
#define ME6000_AO_IRQ_STATUS_REG 0x60 // R/_
#define ME6000_AO_00_IRQ_RESET_REG 0x64 // R/_
#define ME6000_AO_01_IRQ_RESET_REG 0x68 // R/_
#define ME6000_AO_02_IRQ_RESET_REG 0x6C // R/_
#define ME6000_AO_03_IRQ_RESET_REG 0x70 // R/_
#define ME6000_IRQ_STATUS_BIT_0 0x01
#define ME6000_IRQ_STATUS_BIT_1 0x02
#define ME6000_IRQ_STATUS_BIT_2 0x04
#define ME6000_IRQ_STATUS_BIT_3 0x08
#define ME6000_IRQ_STATUS_BIT_AO_HF ME6000_IRQ_STATUS_BIT_0
//DUMY register
#define ME6000_AO_DUMY 0xFC
#endif
#endif

View File

@ -0,0 +1,211 @@
/**
* @file me6000_device.c
*
* @brief Device class template implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "mefirmware.h"
#include "mesubdevice.h"
#include "medebug.h"
#include "medevice.h"
#include "me6000_reg.h"
#include "me6000_device.h"
#include "meplx_reg.h"
#include "me6000_dio.h"
#include "me6000_ao.h"
/**
* @brief Global variable.
* This is working queue for runing a separate atask that will be responsible for work status (start, stop, timeouts).
*/
static struct workqueue_struct *me6000_workqueue;
me_device_t *me6000_pci_constructor(struct pci_dev *pci_device)
{
me6000_device_t *me6000_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
int err;
int i;
int high_range = 0;
int fifo;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me6000_device = kmalloc(sizeof(me6000_device_t), GFP_KERNEL);
if (!me6000_device) {
PERROR("Cannot get memory for device instance.\n");
return NULL;
}
memset(me6000_device, 0, sizeof(me6000_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me6000_device, pci_device);
if (err) {
kfree(me6000_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
/* Download the xilinx firmware */
err = me_xilinx_download(me6000_device->base.info.pci.reg_bases[1],
me6000_device->base.info.pci.reg_bases[2],
&pci_device->dev, "me6000.bin");
if (err) {
me_device_deinit((me_device_t *) me6000_device);
kfree(me6000_device);
PERROR("Can't download firmware.\n");
return NULL;
}
/* Get the index in the device version information table. */
version_idx =
me6000_versions_get_device_index(me6000_device->base.info.pci.
device_id);
// Initialize spin lock .
spin_lock_init(&me6000_device->preload_reg_lock);
spin_lock_init(&me6000_device->dio_ctrl_reg_lock);
/* Create digital input/output instances. */
for (i = 0; i < me6000_versions[version_idx].dio_subdevices; i++) {
subdevice =
(me_subdevice_t *) me6000_dio_constructor(me6000_device->
base.info.pci.
reg_bases[3], i,
&me6000_device->
dio_ctrl_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me6000_device);
kfree(me6000_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me6000_device->base.slist,
subdevice);
}
/* Create analog output instances. */
for (i = 0; i < me6000_versions[version_idx].ao_subdevices; i++) {
high_range = ((i == 8)
&&
((me6000_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME6359)
|| (me6000_device->base.info.pci.device_id ==
PCI_DEVICE_ID_MEILHAUS_ME6259)
)
)? 1 : 0;
fifo =
(i <
me6000_versions[version_idx].
ao_fifo) ? ME6000_AO_HAS_FIFO : 0x0;
fifo |= (i < 4) ? ME6000_AO_EXTRA_HARDWARE : 0x0;
subdevice =
(me_subdevice_t *) me6000_ao_constructor(me6000_device->
base.info.pci.
reg_bases[2],
&me6000_device->
preload_reg_lock,
&me6000_device->
preload_flags,
&me6000_device->
triggering_flags,
i, fifo,
me6000_device->
base.irq,
high_range,
me6000_workqueue);
if (!subdevice) {
me_device_deinit((me_device_t *) me6000_device);
kfree(me6000_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me6000_device->base.slist,
subdevice);
}
return (me_device_t *) me6000_device;
}
// Init and exit of module.
static int __init me6000_init(void)
{
PDEBUG("executed.\n");
me6000_workqueue = create_singlethread_workqueue("me6000");
return 0;
}
static void __exit me6000_exit(void)
{
PDEBUG("executed.\n");
flush_workqueue(me6000_workqueue);
destroy_workqueue(me6000_workqueue);
}
module_init(me6000_init);
module_exit(me6000_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR
("Guenter Gebhardt <g.gebhardt@meilhaus.de> & Krzysztof Gantzke <k.gantzke@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for ME-6000 Device");
MODULE_SUPPORTED_DEVICE("Meilhaus ME-6000 Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me6000_pci_constructor);

View File

@ -0,0 +1,149 @@
/**
* @file me6000_device.h
*
* @brief ME-6000 device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME6000_DEVICE_H
#define _ME6000_DEVICE_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
/**
* @brief Structure holding ME-6000 device capabilities.
*/
typedef struct me6000_version {
uint16_t device_id;
unsigned int dio_subdevices;
unsigned int ao_subdevices;
unsigned int ao_fifo; //How many devices have FIFO
} me6000_version_t;
/**
* @brief ME-6000 device capabilities.
*/
static me6000_version_t me6000_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME6004, 0, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6008, 0, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME600F, 0, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6014, 0, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6018, 0, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME601F, 0, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6034, 0, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6038, 0, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME603F, 0, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6104, 0, 4, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6108, 0, 8, 4},
{PCI_DEVICE_ID_MEILHAUS_ME610F, 0, 16, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6114, 0, 4, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6118, 0, 8, 4},
{PCI_DEVICE_ID_MEILHAUS_ME611F, 0, 16, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6134, 0, 4, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6138, 0, 8, 4},
{PCI_DEVICE_ID_MEILHAUS_ME613F, 0, 16, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6044, 2, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6048, 2, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME604F, 2, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6054, 2, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6058, 2, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME605F, 2, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6074, 2, 4, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6078, 2, 8, 0},
{PCI_DEVICE_ID_MEILHAUS_ME607F, 2, 16, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6144, 2, 4, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6148, 2, 8, 4},
{PCI_DEVICE_ID_MEILHAUS_ME614F, 2, 16, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6154, 2, 4, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6158, 2, 8, 4},
{PCI_DEVICE_ID_MEILHAUS_ME615F, 2, 16, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6174, 2, 4, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6178, 2, 8, 4},
{PCI_DEVICE_ID_MEILHAUS_ME617F, 2, 16, 4},
{PCI_DEVICE_ID_MEILHAUS_ME6259, 2, 9, 0},
{PCI_DEVICE_ID_MEILHAUS_ME6359, 2, 9, 4},
{0},
};
#define ME6000_DEVICE_VERSIONS (sizeof(me6000_versions) / sizeof(me6000_version_t) - 1) /**< Returns the number of entries in #me6000_versions. */
/**
* @brief Returns the index of the device entry in #me6000_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me6000_versions.
*/
static inline unsigned int me6000_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME6000_DEVICE_VERSIONS; i++)
if (me6000_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-6000 device class structure.
*/
typedef struct me6000_device {
me_device_t base; /**< The Meilhaus device base class. */
/* Child class attributes. */
spinlock_t preload_reg_lock; /**< Guards the preload register. */
uint32_t preload_flags;
uint32_t triggering_flags;
spinlock_t dio_ctrl_reg_lock;
} me6000_device_t;
/**
* @brief The ME-6000 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-6000 device instance. \n
* NULL on error.
*/
me_device_t *me6000_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

View File

@ -0,0 +1,415 @@
/**
* @file me6000_dio.c
*
* @brief ME-6000 digital input/output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me6000_dio_reg.h"
#include "me6000_dio.h"
/*
* Defines
*/
/*
* Functions
*/
static int me6000_dio_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me6000_dio_subdevice_t *instance;
uint8_t mode;
PDEBUG("executed.\n");
instance = (me6000_dio_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inb(instance->ctrl_reg);
mode &= ~(0x3 << (instance->dio_idx * 2));
outb(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
spin_unlock(instance->ctrl_reg_lock);
outb(0x00, instance->port_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, 0x00);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me6000_dio_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me6000_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
int size =
flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
| ME_IO_SINGLE_CONFIG_DIO_WORD |
ME_IO_SINGLE_CONFIG_DIO_DWORD);
PDEBUG("executed.\n");
instance = (me6000_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inb(instance->ctrl_reg);
switch (size) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
mode &=
~((ME6000_DIO_CTRL_BIT_MODE_0 |
ME6000_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
} else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
mode &=
~((ME6000_DIO_CTRL_BIT_MODE_0 |
ME6000_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
mode |=
ME6000_DIO_CTRL_BIT_MODE_0 << (instance->
dio_idx * 2);
} else {
PERROR
("Invalid port configuration specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outb(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me6000_dio_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me6000_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
PDEBUG("executed.\n");
instance = (me6000_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inb(instance->
ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
ME6000_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if ((mode ==
(ME6000_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) || !mode) {
*value =
inb(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inb(instance->
ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
ME6000_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if ((mode ==
(ME6000_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) || !mode) {
*value = inb(instance->port_reg) & 0x00FF;
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me6000_dio_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me6000_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
uint8_t byte;
PDEBUG("executed.\n");
instance = (me6000_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inb(instance->
ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
ME6000_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if (mode ==
(ME6000_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) {
byte = inb(instance->port_reg) & 0x00FF;
if (value)
byte |= 0x1 << channel;
else
byte &= ~(0x1 << channel);
outb(byte, instance->port_reg);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inb(instance->
ctrl_reg) & ((ME6000_DIO_CTRL_BIT_MODE_0 |
ME6000_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if (mode ==
(ME6000_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) {
outb(value, instance->port_reg);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me6000_dio_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me6000_dio_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DIO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me6000_dio_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_DIR_BYTE;
return ME_ERRNO_SUCCESS;
}
me6000_dio_subdevice_t *me6000_dio_constructor(uint32_t reg_base,
unsigned int dio_idx,
spinlock_t * ctrl_reg_lock)
{
me6000_dio_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me6000_dio_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me6000_dio_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
/* Set the subdevice ports */
subdevice->ctrl_reg = reg_base + ME6000_DIO_CTRL_REG;
switch (dio_idx) {
case 0:
subdevice->port_reg = reg_base + ME6000_DIO_PORT_0_REG;
break;
case 1:
subdevice->port_reg = reg_base + ME6000_DIO_PORT_1_REG;
break;
default:
err = ME_ERRNO_INVALID_SUBDEVICE;
}
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save digital i/o index */
subdevice->dio_idx = dio_idx;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me6000_dio_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me6000_dio_io_single_config;
subdevice->base.me_subdevice_io_single_read = me6000_dio_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me6000_dio_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me6000_dio_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me6000_dio_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me6000_dio_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,187 @@
/**
* @file me8100_device.c
*
* @brief ME-8100 device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "medevice.h"
#include "me8100_device.h"
#include "mesubdevice.h"
#include "me8100_di.h"
#include "me8100_do.h"
#include "me8254.h"
me_device_t *me8100_pci_constructor(struct pci_dev *pci_device)
{
me8100_device_t *me8100_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
int err;
int i;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me8100_device = kmalloc(sizeof(me8100_device_t), GFP_KERNEL);
if (!me8100_device) {
PERROR("Cannot get memory for device instance.\n");
return NULL;
}
memset(me8100_device, 0, sizeof(me8100_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me8100_device, pci_device);
if (err) {
kfree(me8100_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
/* Get the index in the device version information table. */
version_idx =
me8100_versions_get_device_index(me8100_device->base.info.pci.
device_id);
// Initialize spin lock .
spin_lock_init(&me8100_device->dio_ctrl_reg_lock);
spin_lock_init(&me8100_device->ctr_ctrl_reg_lock);
spin_lock_init(&me8100_device->clk_src_reg_lock);
// Create subdevice instances.
for (i = 0; i < me8100_versions[version_idx].di_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8100_di_constructor(me8100_device->
base.info.pci.
reg_bases[2],
me8100_device->
base.info.pci.
reg_bases[1], i,
me8100_device->
base.irq,
&me8100_device->
dio_ctrl_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me8100_device);
kfree(me8100_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me8100_device->base.slist,
subdevice);
}
for (i = 0; i < me8100_versions[version_idx].do_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8100_do_constructor(me8100_device->
base.info.pci.
reg_bases[2], i,
&me8100_device->
dio_ctrl_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me8100_device);
kfree(me8100_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me8100_device->base.slist,
subdevice);
}
for (i = 0; i < me8100_versions[version_idx].ctr_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8254_constructor(me8100_device->base.
info.pci.device_id,
me8100_device->base.
info.pci.reg_bases[2],
0, i,
&me8100_device->
ctr_ctrl_reg_lock,
&me8100_device->
clk_src_reg_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me8100_device);
kfree(me8100_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me8100_device->base.slist,
subdevice);
}
return (me_device_t *) me8100_device;
}
// Init and exit of module.
static int __init me8100_init(void)
{
PDEBUG("executed.\n.");
return ME_ERRNO_SUCCESS;
}
static void __exit me8100_exit(void)
{
PDEBUG("executed.\n.");
}
module_init(me8100_init);
module_exit(me8100_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for Template Device");
MODULE_SUPPORTED_DEVICE("Meilhaus Template Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me8100_pci_constructor);

View File

@ -0,0 +1,97 @@
/**
* @file me8100_device.h
*
* @brief ME-8100 device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME8100_DEVICE_H
#define _ME8100_DEVICE_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
/**
* @brief Structure holding ME-8100 device capabilities.
*/
typedef struct me8100_version {
uint16_t device_id;
unsigned int di_subdevices;
unsigned int do_subdevices;
unsigned int ctr_subdevices;
} me8100_version_t;
/**
* @brief Device capabilities.
*/
static me8100_version_t me8100_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME8100_A, 1, 1, 3},
{PCI_DEVICE_ID_MEILHAUS_ME8100_B, 2, 2, 3},
{0},
};
#define ME8100_DEVICE_VERSIONS (sizeof(me8100_versions) / sizeof(me8100_version_t) - 1) /**< Returns the number of entries in #me8100_versions. */
/**
* @brief Returns the index of the device entry in #me8100_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me8100_versions.
*/
static inline unsigned int me8100_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME8100_DEVICE_VERSIONS; i++)
if (me8100_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-8100 device class structure.
*/
typedef struct me8100_device {
me_device_t base; /**< The Meilhaus device base class. */
/* Child class attributes. */
spinlock_t dio_ctrl_reg_lock;
spinlock_t ctr_ctrl_reg_lock;
spinlock_t clk_src_reg_lock;
} me8100_device_t;
/**
* @brief The ME-8100 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-8100 device instance. \n
* NULL on error.
*/
me_device_t *me8100_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

View File

@ -0,0 +1,693 @@
/**
* @file me8100_di.c
*
* @brief ME-8100 digital input subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/version.h>
#include "medefines.h"
#include "meerror.h"
#include "meids.h"
#include "medebug.h"
#include "meplx_reg.h"
#include "me8100_reg.h"
#include "me8100_di_reg.h"
#include "me8100_di.h"
/*
* Defines
*/
/*
* Functions
*/
static int me8100_di_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me8100_di_subdevice_t *instance;
unsigned short ctrl;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->ctrl_reg_lock);
ctrl = inw(instance->ctrl_reg);
ctrl &= ~(ME8100_DIO_CTRL_BIT_INTB_1 | ME8100_DIO_CTRL_BIT_INTB_0);
outw(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
spin_unlock(instance->ctrl_reg_lock);
outw(0, instance->mask_reg);
PDEBUG_REG("mask_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->mask_reg - instance->reg_base, 0);
outw(0, instance->pattern_reg);
PDEBUG_REG("pattern_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->pattern_reg - instance->reg_base, 0);
instance->rised = -1;
instance->irq_count = 0;
instance->filtering_flag = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
outl(PLX_INTCSR_LOCAL_INT1_EN |
PLX_INTCSR_LOCAL_INT1_POL |
PLX_INTCSR_LOCAL_INT2_EN |
PLX_INTCSR_LOCAL_INT2_POL |
PLX_INTCSR_PCI_INT_EN, instance->irq_status_reg);
PDEBUG_REG("plx:irq_status_reg outl(0x%lX)=0x%x\n",
instance->irq_status_reg,
PLX_INTCSR_LOCAL_INT1_EN | PLX_INTCSR_LOCAL_INT1_POL |
PLX_INTCSR_LOCAL_INT2_EN | PLX_INTCSR_LOCAL_INT2_POL |
PLX_INTCSR_PCI_INT_EN);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8100_di_io_irq_start(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags)
{
me8100_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint16_t ctrl;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) subdevice;
if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
if (flags &
~(ME_IO_IRQ_START_PATTERN_FILTERING |
ME_IO_IRQ_START_DIO_WORD)) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (irq_edge != ME_IRQ_EDGE_NOT_USED) {
PERROR("Invalid irq edge specified.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
} else if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
if (flags &
~(ME_IO_IRQ_START_EXTENDED_STATUS |
ME_IO_IRQ_START_DIO_WORD)) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (irq_edge != ME_IRQ_EDGE_ANY) {
PERROR("Invalid irq edge specified.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
if (!(irq_arg & 0xFFFF)) {
PERROR("No mask specified.\n");
return ME_ERRNO_INVALID_IRQ_ARG;
}
} else {
PERROR("Invalid irq source specified.\n");
return ME_ERRNO_INVALID_IRQ_SOURCE;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
outw(irq_arg, instance->pattern_reg);
instance->compare_value = irq_arg;
instance->filtering_flag =
(flags & ME_IO_IRQ_START_PATTERN_FILTERING) ? 1 : 0;
}
if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
outw(irq_arg, instance->mask_reg);
}
spin_lock(instance->ctrl_reg_lock);
ctrl = inw(instance->ctrl_reg);
ctrl |= ME8100_DIO_CTRL_BIT_INTB_0;
if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
ctrl &= ~ME8100_DIO_CTRL_BIT_INTB_1;
}
if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
ctrl |= ME8100_DIO_CTRL_BIT_INTB_1;
}
outw(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
spin_unlock(instance->ctrl_reg_lock);
instance->rised = 0;
instance->status_value = 0;
instance->status_value_edges = 0;
instance->line_value = inw(instance->port_reg);
instance->status_flag = flags & ME_IO_IRQ_START_EXTENDED_STATUS;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_di_io_irq_wait(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags)
{
me8100_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
long t = 0;
unsigned long cpu_flags;
int count;
PDEBUG("executed.\n");
PDEVELOP("PID: %d.\n", current->pid);
instance = (me8100_di_subdevice_t *) subdevice;
if (flags &
~(ME_IO_IRQ_WAIT_NORMAL_STATUS | ME_IO_IRQ_WAIT_EXTENDED_STATUS)) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (time_out < 0) {
PERROR("Invalid time_out specified.\n");
return ME_ERRNO_INVALID_TIMEOUT;
}
if (time_out) {
t = (time_out * HZ) / 1000;
if (t == 0)
t = 1;
}
ME_SUBDEVICE_ENTER;
if (instance->rised <= 0) {
instance->rised = 0;
count = instance->irq_count;
if (time_out) {
t = wait_event_interruptible_timeout(instance->
wait_queue,
((count !=
instance->
irq_count)
|| (instance->
rised < 0)),
t);
// t = wait_event_interruptible_timeout(instance->wait_queue, (instance->rised != 0), t);
if (t == 0) {
PERROR("Wait on interrupt timed out.\n");
err = ME_ERRNO_TIMEOUT;
}
} else {
wait_event_interruptible(instance->wait_queue,
((count != instance->irq_count)
|| (instance->rised < 0)));
// wait_event_interruptible(instance->wait_queue, (instance->rised != 0));
}
if (instance->rised < 0) {
PERROR("Wait on interrupt aborted by user.\n");
err = ME_ERRNO_CANCELLED;
}
}
if (signal_pending(current)) {
PERROR("Wait on interrupt aborted by signal.\n");
err = ME_ERRNO_SIGNAL;
}
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
*irq_count = instance->irq_count;
if (!err) {
if (flags & ME_IO_IRQ_WAIT_NORMAL_STATUS) {
*value = instance->status_value;
} else if (flags & ME_IO_IRQ_WAIT_EXTENDED_STATUS) {
*value = instance->status_value_edges;
} else { // Use default
if (!instance->status_flag) {
*value = instance->status_value;
} else {
*value = instance->status_value_edges;
}
}
instance->rised = 0;
/*
instance->status_value = 0;
instance->status_value_edges = 0;
*/
} else {
*value = 0;
}
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_di_io_irq_stop(me_subdevice_t * subdevice,
struct file *filep, int channel, int flags)
{
me8100_di_subdevice_t *instance;
uint16_t ctrl;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->ctrl_reg_lock);
ctrl = inw(instance->ctrl_reg);
ctrl &= ~(ME8100_DIO_CTRL_BIT_INTB_1 | ME8100_DIO_CTRL_BIT_INTB_0);
outw(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
spin_unlock(instance->ctrl_reg_lock);
instance->rised = -1;
instance->status_value = 0;
instance->status_value_edges = 0;
instance->filtering_flag = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8100_di_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me8100_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_WORD:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
} else {
PERROR
("Invalid port configuration specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_di_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me8100_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 16)) {
*value = inw(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inw(instance->port_reg) & 0xFF;
} else if (channel == 1) {
*value = (inw(instance->port_reg) >> 8) & 0xFF;
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_WORD:
if (channel == 0) {
*value = inw(instance->port_reg);
} else {
PERROR("Invalid word number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_di_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 16;
return ME_ERRNO_SUCCESS;
}
static int me8100_di_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DI;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me8100_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_BIT_PATTERN_IRQ | ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_ANY;
return ME_ERRNO_SUCCESS;
}
static void me8100_di_destructor(struct me_subdevice *subdevice)
{
me8100_di_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) subdevice;
free_irq(instance->irq, (void *)instance);
me_subdevice_deinit(&instance->base);
kfree(instance);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me8100_isr(int irq, void *dev_id)
#else
static irqreturn_t me8100_isr(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
me8100_di_subdevice_t *instance;
uint32_t icsr;
uint16_t irq_status;
uint16_t line_value = 0;
uint32_t status_val = 0;
PDEBUG("executed.\n");
instance = (me8100_di_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
icsr = inl(instance->irq_status_reg);
if (instance->di_idx == 0) {
if ((icsr &
(PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_PCI_INT_EN |
PLX_INTCSR_LOCAL_INT1_EN)) !=
(PLX_INTCSR_LOCAL_INT1_STATE | PLX_INTCSR_PCI_INT_EN |
PLX_INTCSR_LOCAL_INT1_EN)) {
PINFO
("%ld Shared interrupt. %s(): idx=0 plx:irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, icsr);
return IRQ_NONE;
}
} else if (instance->di_idx == 1) {
if ((icsr &
(PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_PCI_INT_EN |
PLX_INTCSR_LOCAL_INT2_EN)) !=
(PLX_INTCSR_LOCAL_INT2_STATE | PLX_INTCSR_PCI_INT_EN |
PLX_INTCSR_LOCAL_INT2_EN)) {
PINFO
("%ld Shared interrupt. %s(): idx=1 plx:irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, icsr);
return IRQ_NONE;
}
} else {
PERROR("%s():Wrong interrupt idx=%d csr=0x%X.\n", __FUNCTION__,
instance->di_idx, icsr);
return IRQ_NONE;
}
PDEBUG("me8100_isr():Interrupt from idx=%d occured.\n",
instance->di_idx);
spin_lock(&instance->subdevice_lock);
inw(instance->irq_reset_reg);
line_value = inw(instance->port_reg);
irq_status = instance->line_value ^ line_value;
// Make extended information.
status_val |= (0x00FF & (~(uint16_t) instance->line_value & line_value)) << 16; //Raise
status_val |= (0x00FF & ((uint16_t) instance->line_value & ~line_value)); //Fall
instance->line_value = line_value;
if (instance->rised == 0) {
instance->status_value = irq_status;
instance->status_value_edges = status_val;
} else {
instance->status_value |= irq_status;
instance->status_value_edges |= status_val;
}
if (instance->filtering_flag) { // For compare mode only.
if (instance->compare_value == instance->line_value) {
instance->rised = 1;
instance->irq_count++;
}
} else {
instance->rised = 1;
instance->irq_count++;
}
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return IRQ_HANDLED;
}
me8100_di_subdevice_t *me8100_di_constructor(uint32_t me8100_reg_base,
uint32_t plx_reg_base,
unsigned int di_idx,
int irq,
spinlock_t * ctrl_reg_lock)
{
me8100_di_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me8100_di_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me8100_di_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save the subdevice index. */
subdevice->di_idx = di_idx;
/* Initialize wait queue */
init_waitqueue_head(&subdevice->wait_queue);
/* Register interrupt service routine. */
subdevice->irq = irq;
err = request_irq(subdevice->irq, me8100_isr,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME8100_NAME, (void *)subdevice);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
PINFO("Registered irq=%d.\n", subdevice->irq);
/* Initialize the registers */
subdevice->ctrl_reg =
me8100_reg_base + ME8100_CTRL_REG_A + di_idx * ME8100_REG_OFFSET;
subdevice->port_reg =
me8100_reg_base + ME8100_DI_REG_A + di_idx * ME8100_REG_OFFSET;
subdevice->mask_reg =
me8100_reg_base + ME8100_MASK_REG_A + di_idx * ME8100_REG_OFFSET;
subdevice->pattern_reg =
me8100_reg_base + ME8100_PATTERN_REG_A + di_idx * ME8100_REG_OFFSET;
subdevice->din_int_reg =
me8100_reg_base + ME8100_INT_DI_REG_A + di_idx * ME8100_REG_OFFSET;
subdevice->irq_reset_reg =
me8100_reg_base + ME8100_RES_INT_REG_A + di_idx * ME8100_REG_OFFSET;
subdevice->irq_status_reg = plx_reg_base + PLX_INTCSR;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = me8100_reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_irq_start = me8100_di_io_irq_start;
subdevice->base.me_subdevice_io_irq_wait = me8100_di_io_irq_wait;
subdevice->base.me_subdevice_io_irq_stop = me8100_di_io_irq_stop;
subdevice->base.me_subdevice_io_reset_subdevice =
me8100_di_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me8100_di_io_single_config;
subdevice->base.me_subdevice_io_single_read = me8100_di_io_single_read;
subdevice->base.me_subdevice_query_number_channels =
me8100_di_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me8100_di_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me8100_di_query_subdevice_caps;
subdevice->base.me_subdevice_destructor = me8100_di_destructor;
subdevice->rised = 0;
subdevice->irq_count = 0;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,391 @@
/**
* @file me8100_do.c
*
* @brief ME-8100 digital output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me8100_reg.h"
#include "me8100_do_reg.h"
#include "me8100_do.h"
/*
* Defines
*/
/*
* Functions
*/
static int me8100_do_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me8100_do_subdevice_t *instance;
uint16_t ctrl;
PDEBUG("executed.\n");
instance = (me8100_do_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
ctrl = inw(instance->ctrl_reg);
ctrl &= ME8100_DIO_CTRL_BIT_INTB_1 | ME8100_DIO_CTRL_BIT_INTB_0;
outw(ctrl, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, ctrl);
spin_unlock(instance->ctrl_reg_lock);
outw(0, instance->port_reg);
instance->port_reg_mirror = 0;
PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8100_do_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me8100_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
int config;
PDEBUG("executed.\n");
instance = (me8100_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
config = inw(instance->ctrl_reg);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_WORD:
if (channel == 0) {
if (single_config ==
ME_SINGLE_CONFIG_DIO_HIGH_IMPEDANCE) {
config &= ~(ME8100_DIO_CTRL_BIT_ENABLE_DIO);
} else if (single_config == ME_SINGLE_CONFIG_DIO_SINK) {
config |= ME8100_DIO_CTRL_BIT_ENABLE_DIO;
config &= ~ME8100_DIO_CTRL_BIT_SOURCE;
} else if (single_config == ME_SINGLE_CONFIG_DIO_SOURCE) {
config |=
ME8100_DIO_CTRL_BIT_ENABLE_DIO |
ME8100_DIO_CTRL_BIT_SOURCE;
} else {
PERROR
("Invalid port configuration specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid word number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outw(config, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outw(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, config);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_do_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me8100_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8100_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 16)) {
*value = instance->port_reg_mirror & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = instance->port_reg_mirror & 0xFF;
} else if (channel == 1) {
*value = (instance->port_reg_mirror >> 8) & 0xFF;
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_WORD:
if (channel == 0) {
*value = instance->port_reg_mirror;
} else {
PERROR("Invalid word number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_do_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me8100_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8100_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 16)) {
instance->port_reg_mirror =
value ? (instance->
port_reg_mirror | (0x1 << channel))
: (instance->port_reg_mirror & ~(0x1 << channel));
outw(instance->port_reg_mirror, instance->port_reg);
PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg - instance->reg_base,
instance->port_reg_mirror);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
instance->port_reg_mirror &= ~0xFF;
instance->port_reg_mirror |= value & 0xFF;
outw(instance->port_reg_mirror, instance->port_reg);
PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg - instance->reg_base,
instance->port_reg_mirror);
} else if (channel == 1) {
instance->port_reg_mirror &= ~0xFF00;
instance->port_reg_mirror |= (value << 8) & 0xFF00;
outw(instance->port_reg_mirror, instance->port_reg);
PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg - instance->reg_base,
instance->port_reg_mirror);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_WORD:
if (channel == 0) {
instance->port_reg_mirror = value;
outw(value, instance->port_reg);
PDEBUG_REG("port_reg outw(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg - instance->reg_base,
value);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8100_do_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 16;
return ME_ERRNO_SUCCESS;
}
static int me8100_do_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me8100_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_SINK_SOURCE;
return ME_ERRNO_SUCCESS;
}
me8100_do_subdevice_t *me8100_do_constructor(uint32_t reg_base,
unsigned int do_idx,
spinlock_t * ctrl_reg_lock)
{
me8100_do_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me8100_do_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me8100_do_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
/* Initialize registers */
if (do_idx == 0) {
subdevice->port_reg = reg_base + ME8100_DO_REG_A;
subdevice->ctrl_reg = reg_base + ME8100_CTRL_REG_A;
} else if (do_idx == 1) {
subdevice->port_reg = reg_base + ME8100_DO_REG_B;
subdevice->ctrl_reg = reg_base + ME8100_CTRL_REG_B;
} else {
PERROR("Wrong subdevice idx=%d.\n", do_idx);
kfree(subdevice);
return NULL;
}
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save the subdevice index */
subdevice->do_idx = do_idx;
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me8100_do_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me8100_do_io_single_config;
subdevice->base.me_subdevice_io_single_read = me8100_do_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me8100_do_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me8100_do_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me8100_do_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me8100_do_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,194 @@
/**
* @file me8200_device.c
*
* @brief ME-8200 device class implementation.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
#ifndef MODULE
# define MODULE
#endif
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include "meids.h"
#include "meerror.h"
#include "mecommon.h"
#include "meinternal.h"
#include "medebug.h"
#include "meplx_reg.h"
#include "medevice.h"
#include "me8200_device.h"
#include "mesubdevice.h"
#include "me8200_di.h"
#include "me8200_do.h"
#include "me8200_dio.h"
me_device_t *me8200_pci_constructor(struct pci_dev *pci_device)
{
me8200_device_t *me8200_device;
me_subdevice_t *subdevice;
unsigned int version_idx;
int err;
int i;
PDEBUG("executed.\n");
// Allocate structure for device instance.
me8200_device = kmalloc(sizeof(me8200_device_t), GFP_KERNEL);
if (!me8200_device) {
PERROR("Cannot get memory for device instance.\n");
return NULL;
}
memset(me8200_device, 0, sizeof(me8200_device_t));
// Initialize base class structure.
err = me_device_pci_init((me_device_t *) me8200_device, pci_device);
if (err) {
kfree(me8200_device);
PERROR("Cannot initialize device base class.\n");
return NULL;
}
/* Get the index in the device version information table. */
version_idx =
me8200_versions_get_device_index(me8200_device->base.info.pci.
device_id);
// Initialize spin lock .
spin_lock_init(&me8200_device->irq_ctrl_lock);
spin_lock_init(&me8200_device->irq_mode_lock);
spin_lock_init(&me8200_device->dio_ctrl_lock);
/* Setup the PLX interrupt configuration */
outl(PLX_INTCSR_LOCAL_INT1_EN |
PLX_INTCSR_LOCAL_INT1_POL |
PLX_INTCSR_LOCAL_INT2_EN |
PLX_INTCSR_LOCAL_INT2_POL |
PLX_INTCSR_PCI_INT_EN,
me8200_device->base.info.pci.reg_bases[1] + PLX_INTCSR);
// Create subdevice instances.
for (i = 0; i < me8200_versions[version_idx].di_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8200_di_constructor(me8200_device->
base.info.pci.
reg_bases[2], i,
me8200_device->
base.irq,
&me8200_device->
irq_ctrl_lock,
&me8200_device->
irq_mode_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me8200_device);
kfree(me8200_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me8200_device->base.slist,
subdevice);
}
for (i = 0; i < me8200_versions[version_idx].do_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8200_do_constructor(me8200_device->
base.info.pci.
reg_bases[2], i,
me8200_device->
base.irq,
&me8200_device->
irq_mode_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me8200_device);
kfree(me8200_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me8200_device->base.slist,
subdevice);
}
for (i = 0; i < me8200_versions[version_idx].dio_subdevices; i++) {
subdevice =
(me_subdevice_t *) me8200_dio_constructor(me8200_device->
base.info.pci.
reg_bases[2], i,
&me8200_device->
dio_ctrl_lock);
if (!subdevice) {
me_device_deinit((me_device_t *) me8200_device);
kfree(me8200_device);
PERROR("Cannot get memory for subdevice.\n");
return NULL;
}
me_slist_add_subdevice_tail(&me8200_device->base.slist,
subdevice);
}
return (me_device_t *) me8200_device;
}
// Init and exit of module.
static int __init me8200_init(void)
{
PDEBUG("executed.\n.");
return 0;
}
static void __exit me8200_exit(void)
{
PDEBUG("executed.\n.");
}
module_init(me8200_init);
module_exit(me8200_exit);
// Administrative stuff for modinfo.
MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
MODULE_DESCRIPTION("Device Driver Module for Template Device");
MODULE_SUPPORTED_DEVICE("Meilhaus Template Devices");
MODULE_LICENSE("GPL");
// Export the constructor.
EXPORT_SYMBOL(me8200_pci_constructor);

View File

@ -0,0 +1,97 @@
/**
* @file me8200_device.h
*
* @brief ME-8200 device class.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _ME8200_DEVICE_H
#define _ME8200_DEVICE_H
#include <linux/pci.h>
#include <linux/spinlock.h>
#include "medevice.h"
#ifdef __KERNEL__
/**
* @brief Structure holding ME-8200 device capabilities.
*/
typedef struct me8200_version {
uint16_t device_id;
unsigned int di_subdevices;
unsigned int do_subdevices;
unsigned int dio_subdevices;
} me8200_version_t;
/**
* @brief Device capabilities.
*/
static me8200_version_t me8200_versions[] = {
{PCI_DEVICE_ID_MEILHAUS_ME8200_A, 1, 1, 2},
{PCI_DEVICE_ID_MEILHAUS_ME8200_B, 2, 2, 2},
{0},
};
#define ME8200_DEVICE_VERSIONS (sizeof(me8200_versions) / sizeof(me8200_version_t) - 1) /**< Returns the number of entries in #me8200_versions. */
/**
* @brief Returns the index of the device entry in #me8200_versions.
*
* @param device_id The PCI device id of the device to query.
* @return The index of the device in #me8200_versions.
*/
static inline unsigned int me8200_versions_get_device_index(uint16_t device_id)
{
unsigned int i;
for (i = 0; i < ME8200_DEVICE_VERSIONS; i++)
if (me8200_versions[i].device_id == device_id)
break;
return i;
}
/**
* @brief The ME-8200 device class structure.
*/
typedef struct me8200_device {
me_device_t base; /**< The Meilhaus device base class. */
/* Child class attributes. */
spinlock_t irq_ctrl_lock; /**< Lock for the interrupt control register. */
spinlock_t irq_mode_lock; /**< Lock for the interrupt mode register. */
spinlock_t dio_ctrl_lock; /**< Lock for the digital i/o control register. */
} me8200_device_t;
/**
* @brief The ME-8200 device class constructor.
*
* @param pci_device The pci device structure given by the PCI subsystem.
*
* @return On succes a new ME-8200 device instance. \n
* NULL on error.
*/
me_device_t *me8200_pci_constructor(struct pci_dev *pci_device)
__attribute__ ((weak));
#endif
#endif

View File

@ -0,0 +1,857 @@
/**
* @file me8200_di.c
*
* @brief ME-8200 digital input subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
///Includes
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/version.h>
#include "medefines.h"
#include "meerror.h"
#include "meids.h"
#include "medebug.h"
#include "me8200_reg.h"
#include "me8200_di_reg.h"
#include "me8200_di.h"
/// Defines
static void me8200_di_destructor(struct me_subdevice *subdevice);
static int me8200_di_io_irq_start(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags);
static int me8200_di_io_irq_wait(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags);
static int me8200_di_io_irq_stop(me_subdevice_t * subdevice,
struct file *filep, int channel, int flags);
static int me8200_di_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags);
static int me8200_di_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags);
static int me8200_di_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags);
static int me8200_di_query_number_channels(me_subdevice_t * subdevice,
int *number);
static int me8200_di_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype);
static int me8200_di_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me8200_isr(int irq, void *dev_id);
#else
static irqreturn_t me8200_isr(int irq, void *dev_id, struct pt_regs *regs);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me8200_isr_EX(int irq, void *dev_id);
#else
static irqreturn_t me8200_isr_EX(int irq, void *dev_id, struct pt_regs *regs);
#endif
static void me8200_di_check_version(me8200_di_subdevice_t * instance,
unsigned long addr);
///Functions
static int me8200_di_io_irq_start(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags)
{
me8200_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
volatile uint8_t tmp;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_di_subdevice_t *) subdevice;
if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
if (flags &
~(ME_IO_IRQ_START_PATTERN_FILTERING |
ME_IO_IRQ_START_DIO_BYTE)) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (irq_edge != ME_IRQ_EDGE_NOT_USED) {
PERROR("Invalid irq edge specified.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
} else if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
if (flags &
~(ME_IO_IRQ_START_EXTENDED_STATUS |
ME_IO_IRQ_START_DIO_BYTE)) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if ((irq_edge != ME_IRQ_EDGE_RISING)
&& (irq_edge != ME_IRQ_EDGE_FALLING)
&& (irq_edge != ME_IRQ_EDGE_ANY)) {
PERROR("Invalid irq edge specified.\n");
return ME_ERRNO_INVALID_IRQ_EDGE;
}
if (!(irq_arg & 0xFF)) {
PERROR("No mask specified.\n");
return ME_ERRNO_INVALID_IRQ_ARG;
}
} else {
PERROR("Invalid irq source specified.\n");
return ME_ERRNO_INVALID_IRQ_SOURCE;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
outb(irq_arg, instance->compare_reg);
PDEBUG_REG("compare_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->compare_reg - instance->reg_base, irq_arg);
outb(0xFF, instance->mask_reg);
PDEBUG_REG("mask_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->mask_reg - instance->reg_base, 0xff);
instance->compare_value = irq_arg;
instance->filtering_flag =
(flags & ME_IO_IRQ_START_PATTERN_FILTERING) ? 1 : 0;
}
if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
outb(irq_arg, instance->mask_reg);
PDEBUG_REG("mask_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->mask_reg - instance->reg_base, irq_arg);
instance->filtering_flag = 0;
}
spin_lock(instance->irq_mode_lock);
tmp = inb(instance->irq_mode_reg);
tmp &=
~(ME8200_IRQ_MODE_MASK <<
(ME8200_IRQ_MODE_DI_SHIFT * instance->di_idx));
if (irq_source == ME_IRQ_SOURCE_DIO_PATTERN) {
tmp |=
ME8200_IRQ_MODE_MASK_COMPARE << (ME8200_IRQ_MODE_DI_SHIFT *
instance->di_idx);
}
if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
tmp |=
ME8200_IRQ_MODE_MASK_MASK << (ME8200_IRQ_MODE_DI_SHIFT *
instance->di_idx);
}
outb(tmp, instance->irq_mode_reg);
PDEBUG_REG("irq_mode_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_mode_reg - instance->reg_base, tmp);
spin_unlock(instance->irq_mode_lock);
spin_lock(instance->irq_ctrl_lock);
tmp = inb(instance->irq_ctrl_reg);
tmp |=
(ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
tmp |=
ME8200_DI_IRQ_CTRL_BIT_ENABLE << (ME8200_DI_IRQ_CTRL_SHIFT *
instance->di_idx);
if (irq_source == ME_IRQ_SOURCE_DIO_MASK) {
tmp &=
~(ME8200_DI_IRQ_CTRL_MASK_EDGE <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
if (irq_edge == ME_IRQ_EDGE_RISING) {
tmp |=
ME8200_DI_IRQ_CTRL_MASK_EDGE_RISING <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx);
} else if (irq_edge == ME_IRQ_EDGE_FALLING) {
tmp |=
ME8200_DI_IRQ_CTRL_MASK_EDGE_FALLING <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx);
} else if (irq_edge == ME_IRQ_EDGE_ANY) {
tmp |=
ME8200_DI_IRQ_CTRL_MASK_EDGE_ANY <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx);
}
}
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
tmp &=
~(ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
instance->line_value = inb(instance->port_reg);
spin_unlock(instance->irq_ctrl_lock);
instance->rised = 0;
instance->status_value = 0;
instance->status_value_edges = 0;
instance->status_flag = flags & ME_IO_IRQ_START_EXTENDED_STATUS;
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_di_io_irq_wait(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags)
{
me8200_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
long t = 0;
unsigned long cpu_flags;
int count;
PDEBUG("executed.\n");
PDEVELOP("PID: %d.\n", current->pid);
instance = (me8200_di_subdevice_t *) subdevice;
if (flags &
~(ME_IO_IRQ_WAIT_NORMAL_STATUS | ME_IO_IRQ_WAIT_EXTENDED_STATUS)) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (time_out < 0) {
PERROR("Invalid time_out specified.\n");
return ME_ERRNO_INVALID_TIMEOUT;
}
if (time_out) {
t = (time_out * HZ) / 1000;
if (t == 0)
t = 1;
}
ME_SUBDEVICE_ENTER;
if (instance->rised <= 0) {
instance->rised = 0;
count = instance->count;
if (time_out) {
t = wait_event_interruptible_timeout(instance->
wait_queue,
((count !=
instance->count)
|| (instance->
rised < 0)),
t);
// t = wait_event_interruptible_timeout(instance->wait_queue, (instance->rised != 0), t);
if (t == 0) {
PERROR("Wait on interrupt timed out.\n");
err = ME_ERRNO_TIMEOUT;
}
} else {
wait_event_interruptible(instance->wait_queue,
((count != instance->count)
|| (instance->rised < 0)));
// wait_event_interruptible(instance->wait_queue, (instance->rised != 0));
}
if (instance->rised < 0) {
PERROR("Wait on interrupt aborted by user.\n");
err = ME_ERRNO_CANCELLED;
}
}
if (signal_pending(current)) {
PERROR("Wait on interrupt aborted by signal.\n");
err = ME_ERRNO_SIGNAL;
}
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
*irq_count = instance->count;
if (!err) {
if (flags & ME_IO_IRQ_WAIT_NORMAL_STATUS) {
*value = instance->status_value;
} else if (flags & ME_IO_IRQ_WAIT_EXTENDED_STATUS) {
*value = instance->status_value_edges;
} else { // Use default
if (!instance->status_flag) {
*value = instance->status_value;
} else {
*value = instance->status_value_edges;
}
}
instance->rised = 0;
/*
instance->status_value = 0;
instance->status_value_edges = 0;
*/
} else {
*value = 0;
}
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_di_io_irq_stop(me_subdevice_t * subdevice,
struct file *filep, int channel, int flags)
{
me8200_di_subdevice_t *instance;
uint8_t tmp;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_di_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER spin_lock_irqsave(&instance->subdevice_lock, status);
spin_lock(instance->irq_ctrl_lock);
tmp = inb(instance->irq_ctrl_reg);
tmp |=
(ME8200_DI_IRQ_CTRL_BIT_ENABLE <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
tmp &=
~(ME8200_DI_IRQ_CTRL_BIT_ENABLE <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
tmp |=
(ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
// tmp &= ~(ME8200_DI_IRQ_CTRL_BIT_CLEAR << (ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->irq_ctrl_lock);
instance->rised = -1;
instance->status_value = 0;
instance->status_value_edges = 0;
instance->filtering_flag = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, status);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8200_di_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me8200_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
} else {
PERROR("Invalid port direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_di_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me8200_di_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_di_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inb(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inb(instance->port_reg);
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_di_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me8200_di_subdevice_t *instance = (me8200_di_subdevice_t *) subdevice;
PDEBUG("executed.\n");
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
instance->count = 0;
return me8200_di_io_irq_stop(subdevice, filep, 0, 0);
}
static int me8200_di_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me8200_di_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DI;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me8200_di_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps =
ME_CAPS_DIO_BIT_PATTERN_IRQ |
ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_RISING |
ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_FALLING |
ME_CAPS_DIO_BIT_MASK_IRQ_EDGE_ANY;
return ME_ERRNO_SUCCESS;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me8200_isr(int irq, void *dev_id)
#else
static irqreturn_t me8200_isr(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
me8200_di_subdevice_t *instance;
uint8_t ctrl;
uint8_t irq_status;
uint8_t line_value = 0;
uint8_t line_status = 0;
uint32_t status_val = 0;
instance = (me8200_di_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
irq_status = inb(instance->irq_status_reg);
if (!irq_status) {
PINFO
("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, instance->di_idx, irq_status);
return IRQ_NONE;
}
PDEBUG("executed.\n");
spin_lock(&instance->subdevice_lock);
spin_lock(instance->irq_ctrl_lock);
ctrl = inb(instance->irq_ctrl_reg);
ctrl |=
ME8200_DI_IRQ_CTRL_BIT_CLEAR << (ME8200_DI_IRQ_CTRL_SHIFT *
instance->di_idx);
outb(ctrl, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, ctrl);
ctrl &=
~(ME8200_DI_IRQ_CTRL_BIT_CLEAR <<
(ME8200_DI_IRQ_CTRL_SHIFT * instance->di_idx));
outb(ctrl, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, ctrl);
line_value = inb(instance->port_reg);
spin_unlock(instance->irq_ctrl_lock);
line_status = ((uint8_t) instance->line_value ^ line_value);
// Make extended information.
status_val |= (0x00FF & (~(uint8_t) instance->line_value & line_value)) << 16; //Raise
status_val |= (0x00FF & ((uint8_t) instance->line_value & ~line_value)); //Fall
instance->line_value = (int)line_value;
if (instance->rised == 0) {
instance->status_value = irq_status | line_status;
instance->status_value_edges = status_val;
} else {
instance->status_value |= irq_status | line_status;
instance->status_value_edges |= status_val;
}
if (instance->filtering_flag) { // For compare mode only.
if (instance->compare_value == instance->line_value) {
instance->rised = 1;
instance->count++;
}
} else {
instance->rised = 1;
instance->count++;
}
spin_unlock(&instance->subdevice_lock);
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return IRQ_HANDLED;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me8200_isr_EX(int irq, void *dev_id)
#else
static irqreturn_t me8200_isr_EX(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
me8200_di_subdevice_t *instance;
uint8_t irq_status = 0;
uint16_t irq_status_EX = 0;
uint32_t status_val = 0;
int i, j;
instance = (me8200_di_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
PDEBUG("executed.\n");
//Reset latches. Copy status to extended registers.
irq_status = inb(instance->irq_status_reg);
PDEBUG_REG("idx=%d irq_status_reg=0x%02X\n", instance->di_idx,
irq_status);
if (!irq_status) {
PINFO
("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, instance->di_idx, irq_status);
return IRQ_NONE;
}
irq_status_EX = inb(instance->irq_status_low_reg);
irq_status_EX |= (inb(instance->irq_status_high_reg) << 8);
PDEVELOP("EXTENDED REG: 0x%04x\n", irq_status_EX);
instance->line_value = inb(instance->port_reg);
// Format extended information.
for (i = 0, j = 0; i < 8; i++, j += 2) {
status_val |= ((0x01 << j) & irq_status_EX) >> (j - i); //Fall
status_val |= ((0x01 << (j + 1)) & irq_status_EX) << (15 - j + i); //Raise
}
spin_lock(&instance->subdevice_lock);
if (instance->rised == 0) {
instance->status_value = irq_status;
instance->status_value_edges = status_val;
} else {
instance->status_value |= irq_status;
instance->status_value_edges |= status_val;
}
if (instance->filtering_flag) { // For compare mode only.
if (instance->compare_value == instance->line_value) {
instance->rised = 1;
instance->count++;
}
} else {
instance->rised = 1;
instance->count++;
}
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return IRQ_HANDLED;
}
static void me8200_di_destructor(struct me_subdevice *subdevice)
{
me8200_di_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me8200_di_subdevice_t *) subdevice;
free_irq(instance->irq, (void *)instance);
me_subdevice_deinit(&instance->base);
kfree(instance);
}
me8200_di_subdevice_t *me8200_di_constructor(uint32_t me8200_regbase,
unsigned int di_idx,
int irq,
spinlock_t * irq_ctrl_lock,
spinlock_t * irq_mode_lock)
{
me8200_di_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me8200_di_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me8200_di_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Check firmware version.
me8200_di_check_version(subdevice,
me8200_regbase + ME8200_FIRMWARE_VERSION_REG);
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->irq_ctrl_lock = irq_ctrl_lock;
subdevice->irq_mode_lock = irq_mode_lock;
/* Save the subdevice index. */
subdevice->di_idx = di_idx;
/* Initialize registers */
if (di_idx == 0) {
subdevice->port_reg = me8200_regbase + ME8200_DI_PORT_0_REG;
subdevice->mask_reg = me8200_regbase + ME8200_DI_MASK_0_REG;
subdevice->compare_reg =
me8200_regbase + ME8200_DI_COMPARE_0_REG;
subdevice->irq_status_reg =
me8200_regbase + ME8200_DI_CHANGE_0_REG;
subdevice->irq_status_low_reg =
me8200_regbase + ME8200_DI_EXTEND_CHANGE_0_LOW_REG;
subdevice->irq_status_high_reg =
me8200_regbase + ME8200_DI_EXTEND_CHANGE_0_HIGH_REG;
} else if (di_idx == 1) {
subdevice->port_reg = me8200_regbase + ME8200_DI_PORT_1_REG;
subdevice->mask_reg = me8200_regbase + ME8200_DI_MASK_1_REG;
subdevice->compare_reg =
me8200_regbase + ME8200_DI_COMPARE_1_REG;
subdevice->irq_status_reg =
me8200_regbase + ME8200_DI_CHANGE_1_REG;
subdevice->irq_status_low_reg =
me8200_regbase + ME8200_DI_EXTEND_CHANGE_1_LOW_REG;
subdevice->irq_status_high_reg =
me8200_regbase + ME8200_DI_EXTEND_CHANGE_1_HIGH_REG;
} else {
PERROR("Wrong subdevice idx=%d.\n", di_idx);
kfree(subdevice);
return NULL;
}
subdevice->irq_ctrl_reg = me8200_regbase + ME8200_DI_IRQ_CTRL_REG;
subdevice->irq_mode_reg = me8200_regbase + ME8200_IRQ_MODE_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = me8200_regbase;
#endif
/* Initialize wait queue */
init_waitqueue_head(&subdevice->wait_queue);
/* Overload base class methods. */
subdevice->base.me_subdevice_io_irq_start = me8200_di_io_irq_start;
subdevice->base.me_subdevice_io_irq_wait = me8200_di_io_irq_wait;
subdevice->base.me_subdevice_io_irq_stop = me8200_di_io_irq_stop;
subdevice->base.me_subdevice_io_reset_subdevice =
me8200_di_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me8200_di_io_single_config;
subdevice->base.me_subdevice_io_single_read = me8200_di_io_single_read;
subdevice->base.me_subdevice_query_number_channels =
me8200_di_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me8200_di_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me8200_di_query_subdevice_caps;
subdevice->base.me_subdevice_destructor = me8200_di_destructor;
subdevice->rised = 0;
subdevice->count = 0;
/* Register interrupt service routine. */
subdevice->irq = irq;
if (subdevice->version > 0) { // NEW
err = request_irq(subdevice->irq, me8200_isr_EX,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME8200_NAME, (void *)subdevice);
} else { //OLD
err = request_irq(subdevice->irq, me8200_isr,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME8200_NAME, (void *)subdevice);
}
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
PDEBUG("Registred irq=%d.\n", subdevice->irq);
return subdevice;
}
static void me8200_di_check_version(me8200_di_subdevice_t * instance,
unsigned long addr)
{
PDEBUG("executed.\n");
instance->version = 0x000000FF & inb(addr);
PDEVELOP("me8200 firmware version: %d\n", instance->version);
/// @note Fix for wrong values in this registry.
if ((instance->version < 0x7) || (instance->version > 0x1F))
instance->version = 0x0;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,418 @@
/**
* @file me8200_dio.c
*
* @brief ME-8200 digital input/output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me8200_dio_reg.h"
#include "me8200_dio.h"
/*
* Defines
*/
/*
* Functions
*/
static int me8200_dio_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me8200_dio_subdevice_t *instance;
uint8_t mode;
PDEBUG("executed.\n");
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
instance = (me8200_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inb(instance->ctrl_reg);
mode &= ~(0x3 << (instance->dio_idx * 2));
outb(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
spin_unlock(instance->ctrl_reg_lock);
outb(0x00, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0x00);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8200_dio_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me8200_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint32_t mode;
uint32_t size =
flags & (ME_IO_SINGLE_CONFIG_DIO_BIT | ME_IO_SINGLE_CONFIG_DIO_BYTE
| ME_IO_SINGLE_CONFIG_DIO_WORD |
ME_IO_SINGLE_CONFIG_DIO_DWORD);
PDEBUG("executed.\n");
instance = (me8200_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
mode = inb(instance->ctrl_reg);
switch (size) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
mode &=
~((ME8200_DIO_CTRL_BIT_MODE_0 |
ME8200_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
} else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
mode &=
~((ME8200_DIO_CTRL_BIT_MODE_0 |
ME8200_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
mode |=
ME8200_DIO_CTRL_BIT_MODE_0 << (instance->
dio_idx * 2);
} else {
PERROR
("Invalid port configuration specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid channel number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
if (!err) {
outb(mode, instance->ctrl_reg);
PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->ctrl_reg - instance->reg_base, mode);
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_dio_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me8200_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
PDEBUG("executed.\n");
instance = (me8200_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inb(instance->
ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
ME8200_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if ((mode ==
(ME8200_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) || !mode) {
*value =
inb(instance->
port_reg) & (0x0001 << channel);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inb(instance->
ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
ME8200_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if ((mode ==
(ME8200_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) || !mode) {
*value = inb(instance->port_reg) & 0x00FF;
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_dio_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me8200_dio_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t mode;
uint8_t byte;
PDEBUG("executed.\n");
instance = (me8200_dio_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
mode =
inb(instance->
ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
ME8200_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if (mode ==
(ME8200_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) {
byte = inb(instance->port_reg);
if (value)
byte |= 0x1 << channel;
else
byte &= ~(0x1 << channel);
outb(byte, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg -
instance->reg_base, byte);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
mode =
inb(instance->
ctrl_reg) & ((ME8200_DIO_CTRL_BIT_MODE_0 |
ME8200_DIO_CTRL_BIT_MODE_1) <<
(instance->dio_idx * 2));
if (mode ==
(ME8200_DIO_CTRL_BIT_MODE_0 <<
(instance->dio_idx * 2))) {
outb(value, instance->port_reg);
PDEBUG_REG("port_reg outl(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg -
instance->reg_base, value);
} else {
PERROR("Port not in output or input mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(instance->ctrl_reg_lock);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_dio_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me8200_dio_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DIO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me8200_dio_query_subdevice_caps(me_subdevice_t * subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_DIR_BYTE;
return ME_ERRNO_SUCCESS;
}
me8200_dio_subdevice_t *me8200_dio_constructor(uint32_t reg_base,
unsigned int dio_idx,
spinlock_t * ctrl_reg_lock)
{
me8200_dio_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me8200_dio_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me8200_dio_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save digital i/o index */
subdevice->dio_idx = dio_idx;
/* Save the subdevice index */
subdevice->ctrl_reg = reg_base + ME8200_DIO_CTRL_REG;
subdevice->port_reg = reg_base + ME8200_DIO_PORT_REG + dio_idx;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Overload base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me8200_dio_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me8200_dio_io_single_config;
subdevice->base.me_subdevice_io_single_read = me8200_dio_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me8200_dio_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me8200_dio_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me8200_dio_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me8200_dio_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,600 @@
/**
* @file me8200_do.c
*
* @brief ME-8200 digital output subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
* @author Krzysztof Gantzke (k.gantzke@meilhaus.de)
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/types.h>
#include <linux/version.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "meids.h"
#include "medebug.h"
#include "me8200_reg.h"
#include "me8200_do_reg.h"
#include "me8200_do.h"
/*
* Defines
*/
/*
* Functions
*/
static int me8200_do_io_irq_start(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int irq_source,
int irq_edge, int irq_arg, int flags)
{
me8200_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t tmp;
unsigned long status;
if (flags & ~ME_IO_IRQ_START_DIO_BYTE) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel != 0) {
PERROR("Invalid channel specified.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
if (irq_source != ME_IRQ_SOURCE_DIO_OVER_TEMP) {
PERROR("Invalid interrupt source specified.\n");
return ME_ERRNO_INVALID_IRQ_SOURCE;
}
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
spin_lock(instance->irq_mode_lock);
tmp = inb(instance->irq_ctrl_reg);
tmp |=
ME8200_IRQ_MODE_BIT_ENABLE_POWER << (ME8200_IRQ_MODE_POWER_SHIFT *
instance->do_idx);
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->irq_mode_lock);
instance->rised = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_do_io_irq_wait(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *irq_count,
int *value, int time_out, int flags)
{
me8200_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
long t = 0;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (time_out < 0) {
PERROR("Invalid time_out specified.\n");
return ME_ERRNO_INVALID_TIMEOUT;
}
if (time_out) {
t = (time_out * HZ) / 1000;
if (t == 0)
t = 1;
}
ME_SUBDEVICE_ENTER;
if (instance->rised <= 0) {
instance->rised = 0;
if (time_out) {
t = wait_event_interruptible_timeout(instance->
wait_queue,
(instance->rised !=
0), t);
if (t == 0) {
PERROR
("Wait on external interrupt timed out.\n");
err = ME_ERRNO_TIMEOUT;
}
} else {
wait_event_interruptible(instance->wait_queue,
(instance->rised != 0));
}
if (instance->rised < 0) {
PERROR("Wait on interrupt aborted by user.\n");
err = ME_ERRNO_CANCELLED;
}
}
if (signal_pending(current)) {
PERROR("Wait on external interrupt aborted by signal.\n");
err = ME_ERRNO_SIGNAL;
}
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
instance->rised = 0;
*irq_count = instance->count;
*value = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_do_io_irq_stop(me_subdevice_t * subdevice,
struct file *filep, int channel, int flags)
{
me8200_do_subdevice_t *instance;
uint8_t tmp;
unsigned long cpu_flags;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
spin_lock(instance->irq_mode_lock);
tmp = inb(instance->irq_ctrl_reg);
tmp &=
~(ME8200_IRQ_MODE_BIT_ENABLE_POWER <<
(ME8200_IRQ_MODE_POWER_SHIFT * instance->do_idx));
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->irq_mode_lock);
instance->rised = -1;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8200_do_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me8200_do_subdevice_t *instance;
unsigned long cpu_flags;
uint8_t tmp;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
outb(0x00, instance->port_reg);
PDEBUG_REG("port_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->port_reg - instance->reg_base, 0x00);
spin_lock(instance->irq_mode_lock);
tmp = inb(instance->irq_ctrl_reg);
tmp &=
~(ME8200_IRQ_MODE_BIT_ENABLE_POWER <<
(ME8200_IRQ_MODE_POWER_SHIFT * instance->do_idx));
outb(tmp, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outb(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, tmp);
spin_unlock(instance->irq_mode_lock);
instance->rised = -1;
instance->count = 0;
spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
wake_up_interruptible_all(&instance->wait_queue);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8200_do_io_single_config(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me8200_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
switch (flags) {
case ME_IO_SINGLE_CONFIG_NO_FLAGS:
case ME_IO_SINGLE_CONFIG_DIO_BYTE:
if (channel == 0) {
if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
} else {
PERROR("Invalid byte direction specified.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
} else {
PERROR("Invalid byte specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_do_io_single_read(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me8200_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inb(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inb(instance->port_reg);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_do_io_single_write(me_subdevice_t * subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me8200_do_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
uint8_t state;
unsigned long status;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock_irqsave(&instance->subdevice_lock, status);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
state = inb(instance->port_reg);
state =
value ? (state | (0x1 << channel)) : (state &
~(0x1 <<
channel));
outb(state, instance->port_reg);
PDEBUG_REG("port_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg - instance->reg_base,
state);
} else {
PERROR("Invalid bit number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
outb(value, instance->port_reg);
PDEBUG_REG("port_reg outb(0x%lX+0x%lX)=0x%x\n",
instance->reg_base,
instance->port_reg - instance->reg_base,
value);
} else {
PERROR("Invalid byte number specified.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock_irqrestore(&instance->subdevice_lock, status);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8200_do_query_number_channels(me_subdevice_t * subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = 8;
return ME_ERRNO_SUCCESS;
}
static int me8200_do_query_subdevice_type(me_subdevice_t * subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me8200_do_query_subdevice_caps(me_subdevice_t * subdevice, int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_OVER_TEMP_IRQ;
return ME_ERRNO_SUCCESS;
}
static void me8200_do_destructor(struct me_subdevice *subdevice)
{
me8200_do_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me8200_do_subdevice_t *) subdevice;
free_irq(instance->irq, (void *)instance);
me_subdevice_deinit(&instance->base);
kfree(instance);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
static irqreturn_t me8200_do_isr(int irq, void *dev_id)
#else
static irqreturn_t me8200_do_isr(int irq, void *dev_id, struct pt_regs *regs)
#endif
{
me8200_do_subdevice_t *instance;
uint16_t ctrl;
uint8_t irq_status;
instance = (me8200_do_subdevice_t *) dev_id;
if (irq != instance->irq) {
PERROR("Incorrect interrupt num: %d.\n", irq);
return IRQ_NONE;
}
irq_status = inb(instance->irq_status_reg);
if (!
(irq_status &
(ME8200_DO_IRQ_STATUS_BIT_ACTIVE << instance->do_idx))) {
PINFO
("%ld Shared interrupt. %s(): idx=%d irq_status_reg=0x%04X\n",
jiffies, __FUNCTION__, instance->do_idx, irq_status);
return IRQ_NONE;
}
PDEBUG("executed.\n");
spin_lock(&instance->subdevice_lock);
instance->rised = 1;
instance->count++;
spin_lock(instance->irq_mode_lock);
ctrl = inw(instance->irq_ctrl_reg);
ctrl |= ME8200_IRQ_MODE_BIT_CLEAR_POWER << instance->do_idx;
outw(ctrl, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, ctrl);
ctrl &= ~(ME8200_IRQ_MODE_BIT_CLEAR_POWER << instance->do_idx);
outw(ctrl, instance->irq_ctrl_reg);
PDEBUG_REG("irq_ctrl_reg outw(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
instance->irq_ctrl_reg - instance->reg_base, ctrl);
spin_unlock(instance->irq_mode_lock);
spin_unlock(&instance->subdevice_lock);
wake_up_interruptible_all(&instance->wait_queue);
return IRQ_HANDLED;
}
me8200_do_subdevice_t *me8200_do_constructor(uint32_t reg_base,
unsigned int do_idx,
int irq,
spinlock_t * irq_mode_lock)
{
me8200_do_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me8200_do_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for subdevice instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me8200_do_subdevice_t));
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->irq_mode_lock = irq_mode_lock;
/* Save the index of the digital output */
subdevice->do_idx = do_idx;
subdevice->irq = irq;
/* Initialize the registers */
if (do_idx == 0) {
subdevice->port_reg = reg_base + ME8200_DO_PORT_0_REG;
} else if (do_idx == 1) {
subdevice->port_reg = reg_base + ME8200_DO_PORT_1_REG;
} else {
PERROR("Wrong subdevice idx=%d.\n", do_idx);
kfree(subdevice);
return NULL;
}
subdevice->irq_ctrl_reg = reg_base + ME8200_IRQ_MODE_REG;
subdevice->irq_status_reg = reg_base + ME8200_DO_IRQ_STATUS_REG;
#ifdef MEDEBUG_DEBUG_REG
subdevice->reg_base = reg_base;
#endif
/* Initialize the wait queue */
init_waitqueue_head(&subdevice->wait_queue);
/* Request the interrupt line */
err = request_irq(irq, me8200_do_isr,
#ifdef IRQF_DISABLED
IRQF_DISABLED | IRQF_SHARED,
#else
SA_INTERRUPT | SA_SHIRQ,
#endif
ME8200_NAME, (void *)subdevice);
if (err) {
PERROR("Cannot get interrupt line.\n");
kfree(subdevice);
return NULL;
}
PINFO("Registered irq=%d.\n", irq);
/* Overload base class methods. */
subdevice->base.me_subdevice_io_irq_start = me8200_do_io_irq_start;
subdevice->base.me_subdevice_io_irq_wait = me8200_do_io_irq_wait;
subdevice->base.me_subdevice_io_irq_stop = me8200_do_io_irq_stop;
subdevice->base.me_subdevice_io_reset_subdevice =
me8200_do_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config =
me8200_do_io_single_config;
subdevice->base.me_subdevice_io_single_read = me8200_do_io_single_read;
subdevice->base.me_subdevice_io_single_write =
me8200_do_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me8200_do_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me8200_do_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me8200_do_query_subdevice_caps;
subdevice->base.me_subdevice_destructor = me8200_do_destructor;
subdevice->rised = 0;
subdevice->count = 0;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,462 @@
/**
* @file me8255.c
*
* @brief 8255 subdevice instance.
* @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
* @author Guenter Gebhardt
*/
/*
* Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __KERNEL__
# define __KERNEL__
#endif
/*
* Includes
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/types.h>
#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"
#include "medebug.h"
#include "me8255_reg.h"
#include "me8255.h"
/*
* Defines
*/
/*
* Functions
*/
static uint8_t get_mode_from_mirror(uint32_t mirror)
{
PDEBUG("executed.\n");
if (mirror & ME8255_PORT_0_OUTPUT) {
if (mirror & ME8255_PORT_1_OUTPUT) {
if (mirror & ME8255_PORT_2_OUTPUT) {
return ME8255_MODE_OOO;
} else {
return ME8255_MODE_IOO;
}
} else {
if (mirror & ME8255_PORT_2_OUTPUT) {
return ME8255_MODE_OIO;
} else {
return ME8255_MODE_IIO;
}
}
} else {
if (mirror & ME8255_PORT_1_OUTPUT) {
if (mirror & ME8255_PORT_2_OUTPUT) {
return ME8255_MODE_OOI;
} else {
return ME8255_MODE_IOI;
}
} else {
if (mirror & ME8255_PORT_2_OUTPUT) {
return ME8255_MODE_OII;
} else {
return ME8255_MODE_III;
}
}
}
}
static int me8255_io_reset_subdevice(struct me_subdevice *subdevice,
struct file *filep, int flags)
{
me8255_subdevice_t *instance;
PDEBUG("executed.\n");
instance = (me8255_subdevice_t *) subdevice;
if (flags) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
spin_lock(instance->ctrl_reg_lock);
*instance->ctrl_reg_mirror &=
~(ME8255_PORT_0_OUTPUT << instance->dio_idx);
outb(get_mode_from_mirror(*instance->ctrl_reg_mirror),
instance->ctrl_reg);
spin_unlock(instance->ctrl_reg_lock);
outb(0, instance->port_reg);
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return ME_ERRNO_SUCCESS;
}
static int me8255_io_single_config(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int single_config,
int ref,
int trig_chan,
int trig_type, int trig_edge, int flags)
{
me8255_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8255_subdevice_t *) subdevice;
if (flags & ~ME_IO_SINGLE_CONFIG_DIO_BYTE) {
PERROR("Invalid flag specified.\n");
return ME_ERRNO_INVALID_FLAGS;
}
if (channel) {
PERROR("Invalid channel.\n");
return ME_ERRNO_INVALID_CHANNEL;
}
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
if (single_config == ME_SINGLE_CONFIG_DIO_INPUT) {
spin_lock(instance->ctrl_reg_lock);
*instance->ctrl_reg_mirror &=
~(ME8255_PORT_0_OUTPUT << instance->dio_idx);
outb(get_mode_from_mirror(*instance->ctrl_reg_mirror),
instance->ctrl_reg);
spin_unlock(instance->ctrl_reg_lock);
} else if (single_config == ME_SINGLE_CONFIG_DIO_OUTPUT) {
spin_lock(instance->ctrl_reg_lock);
*instance->ctrl_reg_mirror |=
(ME8255_PORT_0_OUTPUT << instance->dio_idx);
outb(get_mode_from_mirror(*instance->ctrl_reg_mirror),
instance->ctrl_reg);
spin_unlock(instance->ctrl_reg_lock);
} else {
PERROR("Invalid port direction.\n");
err = ME_ERRNO_INVALID_SINGLE_CONFIG;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8255_io_single_read(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int *value, int time_out, int flags)
{
me8255_subdevice_t *instance;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8255_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
*value = inb(instance->port_reg) & (0x1 << channel);
} else {
PERROR("Invalid bit number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
*value = inb(instance->port_reg);
} else {
PERROR("Invalid byte number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8255_io_single_write(struct me_subdevice *subdevice,
struct file *filep,
int channel,
int value, int time_out, int flags)
{
me8255_subdevice_t *instance;
uint8_t byte;
int err = ME_ERRNO_SUCCESS;
PDEBUG("executed.\n");
instance = (me8255_subdevice_t *) subdevice;
ME_SUBDEVICE_ENTER;
spin_lock(&instance->subdevice_lock);
switch (flags) {
case ME_IO_SINGLE_TYPE_DIO_BIT:
if ((channel >= 0) && (channel < 8)) {
if (*instance->
ctrl_reg_mirror & (ME8255_PORT_0_OUTPUT <<
instance->dio_idx)) {
byte = inb(instance->port_reg);
if (value)
byte |= 0x1 << channel;
else
byte &= ~(0x1 << channel);
outb(byte, instance->port_reg);
} else {
PERROR("Port not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid bit number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
case ME_IO_SINGLE_NO_FLAGS:
case ME_IO_SINGLE_TYPE_DIO_BYTE:
if (channel == 0) {
if (*instance->
ctrl_reg_mirror & (ME8255_PORT_0_OUTPUT <<
instance->dio_idx)) {
outb(value, instance->port_reg);
} else {
PERROR("Port not in output mode.\n");
err = ME_ERRNO_PREVIOUS_CONFIG;
}
} else {
PERROR("Invalid byte number.\n");
err = ME_ERRNO_INVALID_CHANNEL;
}
break;
default:
PERROR("Invalid flags specified.\n");
err = ME_ERRNO_INVALID_FLAGS;
}
spin_unlock(&instance->subdevice_lock);
ME_SUBDEVICE_EXIT;
return err;
}
static int me8255_query_number_channels(struct me_subdevice *subdevice,
int *number)
{
PDEBUG("executed.\n");
*number = ME8255_NUMBER_CHANNELS;
return ME_ERRNO_SUCCESS;
}
static int me8255_query_subdevice_type(struct me_subdevice *subdevice,
int *type, int *subtype)
{
PDEBUG("executed.\n");
*type = ME_TYPE_DIO;
*subtype = ME_SUBTYPE_SINGLE;
return ME_ERRNO_SUCCESS;
}
static int me8255_query_subdevice_caps(struct me_subdevice *subdevice,
int *caps)
{
PDEBUG("executed.\n");
*caps = ME_CAPS_DIO_DIR_BYTE;
return ME_ERRNO_SUCCESS;
}
me8255_subdevice_t *me8255_constructor(uint32_t device_id,
uint32_t reg_base,
unsigned int me8255_idx,
unsigned int dio_idx,
int *ctrl_reg_mirror,
spinlock_t * ctrl_reg_lock)
{
me8255_subdevice_t *subdevice;
int err;
PDEBUG("executed.\n");
/* Allocate memory for subdevice instance */
subdevice = kmalloc(sizeof(me8255_subdevice_t), GFP_KERNEL);
if (!subdevice) {
PERROR("Cannot get memory for 8255 instance.\n");
return NULL;
}
memset(subdevice, 0, sizeof(me8255_subdevice_t));
/* Check if counter index is out of range */
if (dio_idx > 2) {
PERROR("DIO index is out of range.\n");
kfree(subdevice);
return NULL;
}
/* Initialize subdevice base class */
err = me_subdevice_init(&subdevice->base);
if (err) {
PERROR("Cannot initialize subdevice base class instance.\n");
kfree(subdevice);
return NULL;
}
// Initialize spin locks.
spin_lock_init(&subdevice->subdevice_lock);
subdevice->ctrl_reg_lock = ctrl_reg_lock;
/* Save the pointer to global port settings */
subdevice->ctrl_reg_mirror = ctrl_reg_mirror;
/* Save type of Meilhaus device */
subdevice->device_id = device_id;
/* Save the indices */
subdevice->me8255_idx = me8255_idx;
subdevice->dio_idx = dio_idx;
/* Do device specific initialization */
switch (device_id) {
case PCI_DEVICE_ID_MEILHAUS_ME1400:
case PCI_DEVICE_ID_MEILHAUS_ME14E0:
case PCI_DEVICE_ID_MEILHAUS_ME140A:
case PCI_DEVICE_ID_MEILHAUS_ME14EA:
/* Check if 8255 index is out of range */
if (me8255_idx > 0) {
PERROR("8255 index is out of range.\n");
me_subdevice_deinit(&subdevice->base);
kfree(subdevice);
return NULL;
}
case PCI_DEVICE_ID_MEILHAUS_ME140B: /* Fall through */
case PCI_DEVICE_ID_MEILHAUS_ME14EB:
/* Check if 8255 index is out of range */
if (me8255_idx > 1) {
PERROR("8255 index is out of range.\n");
me_subdevice_deinit(&subdevice->base);
kfree(subdevice);
return NULL;
}
/* Get the registers */
if (me8255_idx == 0) {
subdevice->ctrl_reg = reg_base + ME1400AB_PORT_A_CTRL;
subdevice->port_reg =
reg_base + ME1400AB_PORT_A_0 + dio_idx;
} else if (me8255_idx == 1) {
subdevice->ctrl_reg = reg_base + ME1400AB_PORT_B_CTRL;
subdevice->port_reg =
reg_base + ME1400AB_PORT_B_0 + dio_idx;
}
break;
case PCI_DEVICE_ID_MEILHAUS_ME140C:
/* Check if 8255 index is out of range */
if (me8255_idx > 0) {
PERROR("8255 index is out of range.\n");
me_subdevice_deinit(&subdevice->base);
kfree(subdevice);
return NULL;
}
case PCI_DEVICE_ID_MEILHAUS_ME140D: /* Fall through */
/* Check if 8255 index is out of range */
if (me8255_idx > 1) {
PERROR("8255 index is out of range.\n");
me_subdevice_deinit(&subdevice->base);
kfree(subdevice);
return NULL;
}
/* Get the registers */
if (me8255_idx == 0) {
subdevice->ctrl_reg = reg_base + ME1400CD_PORT_A_CTRL;
subdevice->port_reg =
reg_base + ME1400CD_PORT_A_0 + dio_idx;
} else if (me8255_idx == 1) {
subdevice->ctrl_reg = reg_base + ME1400CD_PORT_B_CTRL;
subdevice->port_reg =
reg_base + ME1400CD_PORT_B_0 + dio_idx;
}
break;
default:
PERROR("Unknown device type. dev ID: 0x%04x\n", device_id);
me_subdevice_deinit(&subdevice->base);
kfree(subdevice);
return NULL;
}
/* Overload subdevice base class methods. */
subdevice->base.me_subdevice_io_reset_subdevice =
me8255_io_reset_subdevice;
subdevice->base.me_subdevice_io_single_config = me8255_io_single_config;
subdevice->base.me_subdevice_io_single_read = me8255_io_single_read;
subdevice->base.me_subdevice_io_single_write = me8255_io_single_write;
subdevice->base.me_subdevice_query_number_channels =
me8255_query_number_channels;
subdevice->base.me_subdevice_query_subdevice_type =
me8255_query_subdevice_type;
subdevice->base.me_subdevice_query_subdevice_caps =
me8255_query_subdevice_caps;
return subdevice;
}

View File

@ -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

View File

@ -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

View File

@ -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_

Some files were not shown because too many files have changed in this diff Show More