From c0f005888c6663898a040cd922947dd8caa55160 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 21 Mar 2008 14:12:51 -0700 Subject: [PATCH] Staging: add me4000 pci data collection driver Originally written by Guenter Gebhardt TODO: - checkpatch.pl cleanups - sparse cleanups - possible /proc interaction cleanups - more info needed for Kconfig entry - real device id? - module parameter cleanup Cc: Wolfgang Beiter Cc: Guenter Gebhardt Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/me4000/Kconfig | 10 + drivers/staging/me4000/Makefile | 1 + drivers/staging/me4000/README | 13 + drivers/staging/me4000/me4000.c | 6133 +++++++++++++++++++++++++++++++ drivers/staging/me4000/me4000.h | 954 +++++ 7 files changed, 7114 insertions(+) create mode 100644 drivers/staging/me4000/Kconfig create mode 100644 drivers/staging/me4000/Makefile create mode 100644 drivers/staging/me4000/README create mode 100644 drivers/staging/me4000/me4000.c create mode 100644 drivers/staging/me4000/me4000.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 6da76622191c..56c73bcf4770 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -29,4 +29,6 @@ source "drivers/staging/slicoss/Kconfig" source "drivers/staging/sxg/Kconfig" +source "drivers/staging/me4000/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index cd6d6a52751a..97df19be52dc 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_SLICOSS) += slicoss/ obj-$(CONFIG_SXG) += sxg/ +obj-$(CONFIG_ME4000) += me4000/ diff --git a/drivers/staging/me4000/Kconfig b/drivers/staging/me4000/Kconfig new file mode 100644 index 000000000000..5e6c9de1f11a --- /dev/null +++ b/drivers/staging/me4000/Kconfig @@ -0,0 +1,10 @@ +config ME4000 + tristate "Meilhaus ME-4000 support" + default n + depends on PCI + help + This driver supports the Meilhaus ME-4000 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 me4000. diff --git a/drivers/staging/me4000/Makefile b/drivers/staging/me4000/Makefile new file mode 100644 index 000000000000..74487cd7becf --- /dev/null +++ b/drivers/staging/me4000/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ME4000) += me4000.o diff --git a/drivers/staging/me4000/README b/drivers/staging/me4000/README new file mode 100644 index 000000000000..bbb838632204 --- /dev/null +++ b/drivers/staging/me4000/README @@ -0,0 +1,13 @@ + +TODO: + - checkpatch.pl cleanups + - sparse cleanups + - possible /proc interaction cleanups + - more info needed for Kconfig entry + - real device id? + - module parameter cleanup + +Please send patches to Greg Kroah-Hartman +and Cc: Wolfgang Beiter and +Guenter Gebhardt + diff --git a/drivers/staging/me4000/me4000.c b/drivers/staging/me4000/me4000.c new file mode 100644 index 000000000000..862dd7ffb5c0 --- /dev/null +++ b/drivers/staging/me4000/me4000.c @@ -0,0 +1,6133 @@ +/* Device driver for Meilhaus ME-4000 board family. + * ================================================ + * + * Copyright (C) 2003 Meilhaus Electronic GmbH (support@meilhaus.de) + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Author: Guenter Gebhardt + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +/* Include-File for the Meilhaus ME-4000 I/O board */ +#include "me4000.h" +#include "me4000_firmware.h" +#include "me4610_firmware.h" + +/* Administrative stuff for modinfo */ +MODULE_AUTHOR("Guenter Gebhardt "); +MODULE_DESCRIPTION + ("Device Driver Module for Meilhaus ME-4000 boards version 1.0.5"); +MODULE_SUPPORTED_DEVICE("Meilhaus ME-4000 Multi I/O boards"); +MODULE_LICENSE("GPL"); + +/* Board specific data are kept in a global list */ +LIST_HEAD(me4000_board_info_list); + +/* Major Device Numbers. 0 means to get it automatically from the System */ +static int me4000_ao_major_driver_no = 0; +static int me4000_ai_major_driver_no = 0; +static int me4000_dio_major_driver_no = 0; +static int me4000_cnt_major_driver_no = 0; +static int me4000_ext_int_major_driver_no = 0; + +/* Let the user specify a custom major driver number */ +module_param(me4000_ao_major_driver_no, int, 0); +MODULE_PARM_DESC(me4000_ao_major_driver_no, + "Major driver number for analog output (default 0)"); + +module_param(me4000_ai_major_driver_no, int, 0); +MODULE_PARM_DESC(me4000_ai_major_driver_no, + "Major driver number for analog input (default 0)"); + +module_param(me4000_dio_major_driver_no, int, 0); +MODULE_PARM_DESC(me4000_dio_major_driver_no, + "Major driver number digital I/O (default 0)"); + +module_param(me4000_cnt_major_driver_no, int, 0); +MODULE_PARM_DESC(me4000_cnt_major_driver_no, + "Major driver number for counter (default 0)"); + +module_param(me4000_ext_int_major_driver_no, int, 0); +MODULE_PARM_DESC(me4000_ext_int_major_driver_no, + "Major driver number for external interrupt (default 0)"); + +/*----------------------------------------------------------------------------- + Module stuff + ---------------------------------------------------------------------------*/ +int init_module(void); +void cleanup_module(void); + +/*----------------------------------------------------------------------------- + Board detection and initialization + ---------------------------------------------------------------------------*/ +static int me4000_probe(struct pci_dev *dev, const struct pci_device_id *id); +static int me4000_xilinx_download(me4000_info_t *); +static int me4000_reset_board(me4000_info_t *); + +static void clear_board_info_list(void); +static int get_registers(struct pci_dev *dev, me4000_info_t * info); +static int init_board_info(struct pci_dev *dev, me4000_info_t * board_info); +static int alloc_ao_contexts(me4000_info_t * info); +static void release_ao_contexts(me4000_info_t * board_info); +static int alloc_ai_context(me4000_info_t * info); +static int alloc_dio_context(me4000_info_t * info); +static int alloc_cnt_context(me4000_info_t * info); +static int alloc_ext_int_context(me4000_info_t * info); + +/*----------------------------------------------------------------------------- + Stuff used by all device parts + ---------------------------------------------------------------------------*/ +static int me4000_open(struct inode *, struct file *); +static int me4000_release(struct inode *, struct file *); + +static int me4000_get_user_info(me4000_user_info_t *, + me4000_info_t * board_info); +static int me4000_read_procmem(char *, char **, off_t, int, int *, void *); + +/*----------------------------------------------------------------------------- + Analog output stuff + ---------------------------------------------------------------------------*/ +static ssize_t me4000_ao_write_sing(struct file *, const char *, size_t, + loff_t *); +static ssize_t me4000_ao_write_wrap(struct file *, const char *, size_t, + loff_t *); +static ssize_t me4000_ao_write_cont(struct file *, const char *, size_t, + loff_t *); + +static int me4000_ao_ioctl_sing(struct inode *, struct file *, unsigned int, + unsigned long); +static int me4000_ao_ioctl_wrap(struct inode *, struct file *, unsigned int, + unsigned long); +static int me4000_ao_ioctl_cont(struct inode *, struct file *, unsigned int, + unsigned long); + +static unsigned int me4000_ao_poll_cont(struct file *, poll_table *); +static int me4000_ao_fsync_cont(struct file *, struct dentry *, int); + +static int me4000_ao_start(unsigned long *, me4000_ao_context_t *); +static int me4000_ao_stop(me4000_ao_context_t *); +static int me4000_ao_immediate_stop(me4000_ao_context_t *); +static int me4000_ao_timer_set_divisor(u32 *, me4000_ao_context_t *); +static int me4000_ao_preload(me4000_ao_context_t *); +static int me4000_ao_preload_update(me4000_ao_context_t *); +static int me4000_ao_ex_trig_set_edge(int *, me4000_ao_context_t *); +static int me4000_ao_ex_trig_enable(me4000_ao_context_t *); +static int me4000_ao_ex_trig_disable(me4000_ao_context_t *); +static int me4000_ao_prepare(me4000_ao_context_t * ao_info); +static int me4000_ao_reset(me4000_ao_context_t * ao_info); +static int me4000_ao_enable_do(me4000_ao_context_t *); +static int me4000_ao_disable_do(me4000_ao_context_t *); +static int me4000_ao_fsm_state(int *, me4000_ao_context_t *); + +static int me4000_ao_simultaneous_ex_trig(me4000_ao_context_t * ao_context); +static int me4000_ao_simultaneous_sw(me4000_ao_context_t * ao_context); +static int me4000_ao_simultaneous_disable(me4000_ao_context_t * ao_context); +static int me4000_ao_simultaneous_update(me4000_ao_channel_list_t * channels, + me4000_ao_context_t * ao_context); + +static int me4000_ao_synchronous_ex_trig(me4000_ao_context_t * ao_context); +static int me4000_ao_synchronous_sw(me4000_ao_context_t * ao_context); +static int me4000_ao_synchronous_disable(me4000_ao_context_t * ao_context); + +static int me4000_ao_ex_trig_timeout(unsigned long *arg, + me4000_ao_context_t * ao_context); +static int me4000_ao_get_free_buffer(unsigned long *arg, + me4000_ao_context_t * ao_context); + +/*----------------------------------------------------------------------------- + Analog input stuff + ---------------------------------------------------------------------------*/ +static int me4000_ai_single(me4000_ai_single_t *, me4000_ai_context_t *); +static int me4000_ai_ioctl_sing(struct inode *, struct file *, unsigned int, + unsigned long); + +static ssize_t me4000_ai_read(struct file *, char *, size_t, loff_t *); +static int me4000_ai_ioctl_sw(struct inode *, struct file *, unsigned int, + unsigned long); +static unsigned int me4000_ai_poll(struct file *, poll_table *); +static int me4000_ai_fasync(int fd, struct file *file_p, int mode); + +static int me4000_ai_ioctl_ext(struct inode *, struct file *, unsigned int, + unsigned long); + +static int me4000_ai_prepare(me4000_ai_context_t * ai_context); +static int me4000_ai_reset(me4000_ai_context_t * ai_context); +static int me4000_ai_config(me4000_ai_config_t *, me4000_ai_context_t *); +static int me4000_ai_start(me4000_ai_context_t *); +static int me4000_ai_start_ex(unsigned long *, me4000_ai_context_t *); +static int me4000_ai_stop(me4000_ai_context_t *); +static int me4000_ai_immediate_stop(me4000_ai_context_t *); +static int me4000_ai_ex_trig_enable(me4000_ai_context_t *); +static int me4000_ai_ex_trig_disable(me4000_ai_context_t *); +static int me4000_ai_ex_trig_setup(me4000_ai_trigger_t *, + me4000_ai_context_t *); +static int me4000_ai_sc_setup(me4000_ai_sc_t * arg, + me4000_ai_context_t * ai_context); +static int me4000_ai_offset_enable(me4000_ai_context_t * ai_context); +static int me4000_ai_offset_disable(me4000_ai_context_t * ai_context); +static int me4000_ai_fullscale_enable(me4000_ai_context_t * ai_context); +static int me4000_ai_fullscale_disable(me4000_ai_context_t * ai_context); +static int me4000_ai_fsm_state(int *arg, me4000_ai_context_t * ai_context); +static int me4000_ai_get_count_buffer(unsigned long *arg, + me4000_ai_context_t * ai_context); + +/*----------------------------------------------------------------------------- + EEPROM stuff + ---------------------------------------------------------------------------*/ +static int me4000_eeprom_read(me4000_eeprom_t * arg, + me4000_ai_context_t * ai_context); +static int me4000_eeprom_write(me4000_eeprom_t * arg, + me4000_ai_context_t * ai_context); +static unsigned short eeprom_read_cmd(me4000_ai_context_t * ai_context, + unsigned long cmd, int length); +static int eeprom_write_cmd(me4000_ai_context_t * ai_context, unsigned long cmd, + int length); + +/*----------------------------------------------------------------------------- + Digital I/O stuff + ---------------------------------------------------------------------------*/ +static int me4000_dio_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); +static int me4000_dio_config(me4000_dio_config_t *, me4000_dio_context_t *); +static int me4000_dio_get_byte(me4000_dio_byte_t *, me4000_dio_context_t *); +static int me4000_dio_set_byte(me4000_dio_byte_t *, me4000_dio_context_t *); +static int me4000_dio_reset(me4000_dio_context_t *); + +/*----------------------------------------------------------------------------- + Counter stuff + ---------------------------------------------------------------------------*/ +static int me4000_cnt_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); +static int me4000_cnt_config(me4000_cnt_config_t *, me4000_cnt_context_t *); +static int me4000_cnt_read(me4000_cnt_t *, me4000_cnt_context_t *); +static int me4000_cnt_write(me4000_cnt_t *, me4000_cnt_context_t *); +static int me4000_cnt_reset(me4000_cnt_context_t *); + +/*----------------------------------------------------------------------------- + External interrupt routines + ---------------------------------------------------------------------------*/ +static int me4000_ext_int_ioctl(struct inode *, struct file *, unsigned int, + unsigned long); +static int me4000_ext_int_enable(me4000_ext_int_context_t *); +static int me4000_ext_int_disable(me4000_ext_int_context_t *); +static int me4000_ext_int_count(unsigned long *arg, + me4000_ext_int_context_t * ext_int_context); +static int me4000_ext_int_fasync(int fd, struct file *file_ptr, int mode); + +/*----------------------------------------------------------------------------- + The interrupt service routines + ---------------------------------------------------------------------------*/ +static irqreturn_t me4000_ao_isr(int, void *); +static irqreturn_t me4000_ai_isr(int, void *); +static irqreturn_t me4000_ext_int_isr(int, void *); + +/*----------------------------------------------------------------------------- + Inline functions + ---------------------------------------------------------------------------*/ +static int inline me4000_buf_count(me4000_circ_buf_t, int); +static int inline me4000_buf_space(me4000_circ_buf_t, int); +static int inline me4000_space_to_end(me4000_circ_buf_t, int); +static int inline me4000_values_to_end(me4000_circ_buf_t, int); + +static void inline me4000_outb(unsigned char value, unsigned long port); +static void inline me4000_outl(unsigned long value, unsigned long port); +static unsigned long inline me4000_inl(unsigned long port); +static unsigned char inline me4000_inb(unsigned long port); + +static int me4000_buf_count(me4000_circ_buf_t buf, int size) +{ + return ((buf.head - buf.tail) & (size - 1)); +} + +static int me4000_buf_space(me4000_circ_buf_t buf, int size) +{ + return ((buf.tail - (buf.head + 1)) & (size - 1)); +} + +static int me4000_values_to_end(me4000_circ_buf_t buf, int size) +{ + int end; + int n; + end = size - buf.tail; + n = (buf.head + end) & (size - 1); + return (n < end) ? n : end; +} + +static int me4000_space_to_end(me4000_circ_buf_t buf, int size) +{ + int end; + int n; + + end = size - 1 - buf.head; + n = (end + buf.tail) & (size - 1); + return (n <= end) ? n : (end + 1); +} + +static void me4000_outb(unsigned char value, unsigned long port) +{ + PORT_PDEBUG("--> 0x%02X port 0x%04lX\n", value, port); + outb(value, port); +} + +static void me4000_outl(unsigned long value, unsigned long port) +{ + PORT_PDEBUG("--> 0x%08lX port 0x%04lX\n", value, port); + outl(value, port); +} + +static unsigned long me4000_inl(unsigned long port) +{ + unsigned long value; + value = inl(port); + PORT_PDEBUG("<-- 0x%08lX port 0x%04lX\n", value, port); + return value; +} + +static unsigned char me4000_inb(unsigned long port) +{ + unsigned char value; + value = inb(port); + PORT_PDEBUG("<-- 0x%08X port 0x%04lX\n", value, port); + return value; +} + +struct pci_driver me4000_driver = { + .name = ME4000_NAME, + .id_table = me4000_pci_table, + .probe = me4000_probe +}; + +static struct file_operations me4000_ao_fops_sing = { + owner:THIS_MODULE, + write:me4000_ao_write_sing, + ioctl:me4000_ao_ioctl_sing, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_ao_fops_wrap = { + owner:THIS_MODULE, + write:me4000_ao_write_wrap, + ioctl:me4000_ao_ioctl_wrap, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_ao_fops_cont = { + owner:THIS_MODULE, + write:me4000_ao_write_cont, + poll:me4000_ao_poll_cont, + ioctl:me4000_ao_ioctl_cont, + open:me4000_open, + release:me4000_release, + fsync:me4000_ao_fsync_cont, +}; + +static struct file_operations me4000_ai_fops_sing = { + owner:THIS_MODULE, + ioctl:me4000_ai_ioctl_sing, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_ai_fops_cont_sw = { + owner:THIS_MODULE, + read:me4000_ai_read, + poll:me4000_ai_poll, + ioctl:me4000_ai_ioctl_sw, + open:me4000_open, + release:me4000_release, + fasync:me4000_ai_fasync, +}; + +static struct file_operations me4000_ai_fops_cont_et = { + owner:THIS_MODULE, + read:me4000_ai_read, + poll:me4000_ai_poll, + ioctl:me4000_ai_ioctl_ext, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_ai_fops_cont_et_value = { + owner:THIS_MODULE, + read:me4000_ai_read, + poll:me4000_ai_poll, + ioctl:me4000_ai_ioctl_ext, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_ai_fops_cont_et_chanlist = { + owner:THIS_MODULE, + read:me4000_ai_read, + poll:me4000_ai_poll, + ioctl:me4000_ai_ioctl_ext, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_dio_fops = { + owner:THIS_MODULE, + ioctl:me4000_dio_ioctl, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_cnt_fops = { + owner:THIS_MODULE, + ioctl:me4000_cnt_ioctl, + open:me4000_open, + release:me4000_release, +}; + +static struct file_operations me4000_ext_int_fops = { + owner:THIS_MODULE, + ioctl:me4000_ext_int_ioctl, + open:me4000_open, + release:me4000_release, + fasync:me4000_ext_int_fasync, +}; + +static struct file_operations *me4000_ao_fops_array[] = { + &me4000_ao_fops_sing, // single operations + &me4000_ao_fops_wrap, // wraparound operations + &me4000_ao_fops_cont, // continous operations +}; + +static struct file_operations *me4000_ai_fops_array[] = { + &me4000_ai_fops_sing, // single operations + &me4000_ai_fops_cont_sw, // continuous operations with software start + &me4000_ai_fops_cont_et, // continous operations with external trigger + &me4000_ai_fops_cont_et_value, // sample values by external trigger + &me4000_ai_fops_cont_et_chanlist, // work through one channel list by external trigger +}; + +int __init me4000_init_module(void) +{ + int result = 0; + + CALL_PDEBUG("init_module() is executed\n"); + + /* Register driver capabilities */ + result = pci_register_driver(&me4000_driver); + PDEBUG("init_module():%d devices detected\n", result); + if (result < 0) { + printk(KERN_ERR "ME4000:init_module():Can't register driver\n"); + goto INIT_ERROR_1; + } + + /* Allocate major number for analog output */ + result = + register_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME, + &me4000_ao_fops_sing); + if (result < 0) { + printk(KERN_ERR "ME4000:init_module():Can't get AO major no\n"); + goto INIT_ERROR_2; + } else { + me4000_ao_major_driver_no = result; + } + PDEBUG("init_module():Major driver number for AO = %ld\n", + me4000_ao_major_driver_no); + + /* Allocate major number for analog input */ + result = + register_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME, + &me4000_ai_fops_sing); + if (result < 0) { + printk(KERN_ERR "ME4000:init_module():Can't get AI major no\n"); + goto INIT_ERROR_3; + } else { + me4000_ai_major_driver_no = result; + } + PDEBUG("init_module():Major driver number for AI = %ld\n", + me4000_ai_major_driver_no); + + /* Allocate major number for digital I/O */ + result = + register_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME, + &me4000_dio_fops); + if (result < 0) { + printk(KERN_ERR + "ME4000:init_module():Can't get DIO major no\n"); + goto INIT_ERROR_4; + } else { + me4000_dio_major_driver_no = result; + } + PDEBUG("init_module():Major driver number for DIO = %ld\n", + me4000_dio_major_driver_no); + + /* Allocate major number for counter */ + result = + register_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME, + &me4000_cnt_fops); + if (result < 0) { + printk(KERN_ERR + "ME4000:init_module():Can't get CNT major no\n"); + goto INIT_ERROR_5; + } else { + me4000_cnt_major_driver_no = result; + } + PDEBUG("init_module():Major driver number for CNT = %ld\n", + me4000_cnt_major_driver_no); + + /* Allocate major number for external interrupt */ + result = + register_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME, + &me4000_ext_int_fops); + if (result < 0) { + printk(KERN_ERR + "ME4000:init_module():Can't get major no for external interrupt\n"); + goto INIT_ERROR_6; + } else { + me4000_ext_int_major_driver_no = result; + } + PDEBUG + ("init_module():Major driver number for external interrupt = %ld\n", + me4000_ext_int_major_driver_no); + + /* Create the /proc/me4000 entry */ + if (!create_proc_read_entry + ("me4000", 0, NULL, me4000_read_procmem, NULL)) { + result = -ENODEV; + printk(KERN_ERR + "ME4000:init_module():Can't create proc entry\n"); + goto INIT_ERROR_7; + } + + return 0; + + INIT_ERROR_7: + unregister_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME); + + INIT_ERROR_6: + unregister_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME); + + INIT_ERROR_5: + unregister_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME); + + INIT_ERROR_4: + unregister_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME); + + INIT_ERROR_3: + unregister_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME); + + INIT_ERROR_2: + pci_unregister_driver(&me4000_driver); + clear_board_info_list(); + + INIT_ERROR_1: + return result; +} + +module_init(me4000_init_module); + +static void clear_board_info_list(void) +{ + struct list_head *board_p; + struct list_head *dac_p; + me4000_info_t *board_info; + me4000_ao_context_t *ao_context; + + /* Clear context lists */ + for (board_p = me4000_board_info_list.next; + board_p != &me4000_board_info_list; board_p = board_p->next) { + board_info = list_entry(board_p, me4000_info_t, list); + /* Clear analog output context list */ + while (!list_empty(&board_info->ao_context_list)) { + dac_p = board_info->ao_context_list.next; + ao_context = + list_entry(dac_p, me4000_ao_context_t, list); + me4000_ao_reset(ao_context); + free_irq(ao_context->irq, ao_context); + if (ao_context->circ_buf.buf) + kfree(ao_context->circ_buf.buf); + list_del(dac_p); + kfree(ao_context); + } + + /* Clear analog input context */ + if (board_info->ai_context->circ_buf.buf) + kfree(board_info->ai_context->circ_buf.buf); + kfree(board_info->ai_context); + + /* Clear digital I/O context */ + kfree(board_info->dio_context); + + /* Clear counter context */ + kfree(board_info->cnt_context); + + /* Clear external interrupt context */ + kfree(board_info->ext_int_context); + } + + /* Clear the board info list */ + while (!list_empty(&me4000_board_info_list)) { + board_p = me4000_board_info_list.next; + board_info = list_entry(board_p, me4000_info_t, list); + pci_release_regions(board_info->pci_dev_p); + list_del(board_p); + kfree(board_info); + } +} + +static int get_registers(struct pci_dev *dev, me4000_info_t * board_info) +{ + + /*--------------------------- plx regbase ---------------------------------*/ + + board_info->plx_regbase = pci_resource_start(dev, 1); + if (board_info->plx_regbase == 0) { + printk(KERN_ERR + "ME4000:get_registers():PCI base address 1 is not available\n"); + return -ENODEV; + } + board_info->plx_regbase_size = pci_resource_len(dev, 1); + + PDEBUG + ("get_registers():PLX configuration registers at address 0x%4lX [0x%4lX]\n", + board_info->plx_regbase, board_info->plx_regbase_size); + + /*--------------------------- me4000 regbase ------------------------------*/ + + board_info->me4000_regbase = pci_resource_start(dev, 2); + if (board_info->me4000_regbase == 0) { + printk(KERN_ERR + "ME4000:get_registers():PCI base address 2 is not available\n"); + return -ENODEV; + } + board_info->me4000_regbase_size = pci_resource_len(dev, 2); + + PDEBUG("get_registers():ME4000 registers at address 0x%4lX [0x%4lX]\n", + board_info->me4000_regbase, board_info->me4000_regbase_size); + + /*--------------------------- timer regbase ------------------------------*/ + + board_info->timer_regbase = pci_resource_start(dev, 3); + if (board_info->timer_regbase == 0) { + printk(KERN_ERR + "ME4000:get_registers():PCI base address 3 is not available\n"); + return -ENODEV; + } + board_info->timer_regbase_size = pci_resource_len(dev, 3); + + PDEBUG("get_registers():Timer registers at address 0x%4lX [0x%4lX]\n", + board_info->timer_regbase, board_info->timer_regbase_size); + + /*--------------------------- program regbase ------------------------------*/ + + board_info->program_regbase = pci_resource_start(dev, 5); + if (board_info->program_regbase == 0) { + printk(KERN_ERR + "get_registers():ME4000:PCI base address 5 is not available\n"); + return -ENODEV; + } + board_info->program_regbase_size = pci_resource_len(dev, 5); + + PDEBUG("get_registers():Program registers at address 0x%4lX [0x%4lX]\n", + board_info->program_regbase, board_info->program_regbase_size); + + return 0; +} + +static int init_board_info(struct pci_dev *pci_dev_p, + me4000_info_t * board_info) +{ + int i; + int result; + struct list_head *board_p; + board_info->pci_dev_p = pci_dev_p; + + for (i = 0; i < ME4000_BOARD_VERSIONS; i++) { + if (me4000_boards[i].device_id == pci_dev_p->device) { + board_info->board_p = &me4000_boards[i]; + break; + } + } + if (i == ME4000_BOARD_VERSIONS) { + printk(KERN_ERR + "ME4000:init_board_info():Device ID not valid\n"); + return -ENODEV; + } + + /* Get the index of the board in the global list */ + for (board_p = me4000_board_info_list.next, i = 0; + board_p != &me4000_board_info_list; board_p = board_p->next, i++) { + if (board_p == &board_info->list) { + board_info->board_count = i; + break; + } + } + if (board_p == &me4000_board_info_list) { + printk(KERN_ERR + "ME4000:init_board_info():Cannot get index of baord\n"); + return -ENODEV; + } + + /* Init list head for analog output contexts */ + INIT_LIST_HEAD(&board_info->ao_context_list); + + /* Init spin locks */ + spin_lock_init(&board_info->preload_lock); + spin_lock_init(&board_info->ai_ctrl_lock); + + /* Get the serial number */ + result = pci_read_config_dword(pci_dev_p, 0x2C, &board_info->serial_no); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING + "ME4000:init_board_info: Can't get serial_no\n"); + return result; + } + PDEBUG("init_board_info():serial_no = 0x%x\n", board_info->serial_no); + + /* Get the hardware revision */ + result = + pci_read_config_byte(pci_dev_p, 0x08, &board_info->hw_revision); + if (result != PCIBIOS_SUCCESSFUL) { + printk(KERN_WARNING + "ME4000:init_board_info():Can't get hw_revision\n"); + return result; + } + PDEBUG("init_board_info():hw_revision = 0x%x\n", + board_info->hw_revision); + + /* Get the vendor id */ + board_info->vendor_id = pci_dev_p->vendor; + PDEBUG("init_board_info():vendor_id = 0x%x\n", board_info->vendor_id); + + /* Get the device id */ + board_info->device_id = pci_dev_p->device; + PDEBUG("init_board_info():device_id = 0x%x\n", board_info->device_id); + + /* Get the pci device number */ + board_info->pci_dev_no = PCI_FUNC(pci_dev_p->devfn); + PDEBUG("init_board_info():pci_func_no = 0x%x\n", + board_info->pci_func_no); + + /* Get the pci slot number */ + board_info->pci_dev_no = PCI_SLOT(pci_dev_p->devfn); + PDEBUG("init_board_info():pci_dev_no = 0x%x\n", board_info->pci_dev_no); + + /* Get the pci bus number */ + board_info->pci_bus_no = pci_dev_p->bus->number; + PDEBUG("init_board_info():pci_bus_no = 0x%x\n", board_info->pci_bus_no); + + /* Get the irq assigned to the board */ + board_info->irq = pci_dev_p->irq; + PDEBUG("init_board_info():irq = %d\n", board_info->irq); + + return 0; +} + +static int alloc_ao_contexts(me4000_info_t * info) +{ + int i; + int err; + me4000_ao_context_t *ao_context; + + for (i = 0; i < info->board_p->ao.count; i++) { + ao_context = kmalloc(sizeof(me4000_ao_context_t), GFP_KERNEL); + if (!ao_context) { + printk(KERN_ERR + "alloc_ao_contexts():Can't get memory for ao context\n"); + release_ao_contexts(info); + return -ENOMEM; + } + memset(ao_context, 0, sizeof(me4000_ao_context_t)); + + spin_lock_init(&ao_context->use_lock); + spin_lock_init(&ao_context->int_lock); + ao_context->irq = info->irq; + init_waitqueue_head(&ao_context->wait_queue); + ao_context->board_info = info; + + if (info->board_p->ao.fifo_count) { + /* Allocate circular buffer */ + ao_context->circ_buf.buf = + kmalloc(ME4000_AO_BUFFER_SIZE, GFP_KERNEL); + if (!ao_context->circ_buf.buf) { + printk(KERN_ERR + "alloc_ao_contexts():Can't get circular buffer\n"); + release_ao_contexts(info); + return -ENOMEM; + } + memset(ao_context->circ_buf.buf, 0, + ME4000_AO_BUFFER_SIZE); + + /* Clear the circular buffer */ + ao_context->circ_buf.head = 0; + ao_context->circ_buf.tail = 0; + } + + switch (i) { + case 0: + ao_context->ctrl_reg = + info->me4000_regbase + ME4000_AO_00_CTRL_REG; + ao_context->status_reg = + info->me4000_regbase + ME4000_AO_00_STATUS_REG; + ao_context->fifo_reg = + info->me4000_regbase + ME4000_AO_00_FIFO_REG; + ao_context->single_reg = + info->me4000_regbase + ME4000_AO_00_SINGLE_REG; + ao_context->timer_reg = + info->me4000_regbase + ME4000_AO_00_TIMER_REG; + ao_context->irq_status_reg = + info->me4000_regbase + ME4000_IRQ_STATUS_REG; + ao_context->preload_reg = + info->me4000_regbase + ME4000_AO_LOADSETREG_XX; + break; + case 1: + ao_context->ctrl_reg = + info->me4000_regbase + ME4000_AO_01_CTRL_REG; + ao_context->status_reg = + info->me4000_regbase + ME4000_AO_01_STATUS_REG; + ao_context->fifo_reg = + info->me4000_regbase + ME4000_AO_01_FIFO_REG; + ao_context->single_reg = + info->me4000_regbase + ME4000_AO_01_SINGLE_REG; + ao_context->timer_reg = + info->me4000_regbase + ME4000_AO_01_TIMER_REG; + ao_context->irq_status_reg = + info->me4000_regbase + ME4000_IRQ_STATUS_REG; + ao_context->preload_reg = + info->me4000_regbase + ME4000_AO_LOADSETREG_XX; + break; + case 2: + ao_context->ctrl_reg = + info->me4000_regbase + ME4000_AO_02_CTRL_REG; + ao_context->status_reg = + info->me4000_regbase + ME4000_AO_02_STATUS_REG; + ao_context->fifo_reg = + info->me4000_regbase + ME4000_AO_02_FIFO_REG; + ao_context->single_reg = + info->me4000_regbase + ME4000_AO_02_SINGLE_REG; + ao_context->timer_reg = + info->me4000_regbase + ME4000_AO_02_TIMER_REG; + ao_context->irq_status_reg = + info->me4000_regbase + ME4000_IRQ_STATUS_REG; + ao_context->preload_reg = + info->me4000_regbase + ME4000_AO_LOADSETREG_XX; + break; + case 3: + ao_context->ctrl_reg = + info->me4000_regbase + ME4000_AO_03_CTRL_REG; + ao_context->status_reg = + info->me4000_regbase + ME4000_AO_03_STATUS_REG; + ao_context->fifo_reg = + info->me4000_regbase + ME4000_AO_03_FIFO_REG; + ao_context->single_reg = + info->me4000_regbase + ME4000_AO_03_SINGLE_REG; + ao_context->timer_reg = + info->me4000_regbase + ME4000_AO_03_TIMER_REG; + ao_context->irq_status_reg = + info->me4000_regbase + ME4000_IRQ_STATUS_REG; + ao_context->preload_reg = + info->me4000_regbase + ME4000_AO_LOADSETREG_XX; + break; + default: + break; + } + + if (info->board_p->ao.fifo_count) { + /* Request the interrupt line */ + err = + request_irq(ao_context->irq, me4000_ao_isr, + IRQF_DISABLED | IRQF_SHARED, + ME4000_NAME, ao_context); + if (err) { + printk(KERN_ERR + "alloc_ao_contexts():Can't get interrupt line"); + if (ao_context->circ_buf.buf) + kfree(ao_context->circ_buf.buf); + kfree(ao_context); + release_ao_contexts(info); + return -ENODEV; + } + } + + list_add_tail(&ao_context->list, &info->ao_context_list); + ao_context->index = i; + } + + return 0; +} + +static void release_ao_contexts(me4000_info_t * board_info) +{ + struct list_head *dac_p; + me4000_ao_context_t *ao_context; + + /* Clear analog output context list */ + while (!list_empty(&board_info->ao_context_list)) { + dac_p = board_info->ao_context_list.next; + ao_context = list_entry(dac_p, me4000_ao_context_t, list); + free_irq(ao_context->irq, ao_context); + if (ao_context->circ_buf.buf) + kfree(ao_context->circ_buf.buf); + list_del(dac_p); + kfree(ao_context); + } +} + +static int alloc_ai_context(me4000_info_t * info) +{ + me4000_ai_context_t *ai_context; + + if (info->board_p->ai.count) { + ai_context = kmalloc(sizeof(me4000_ai_context_t), GFP_KERNEL); + if (!ai_context) { + printk(KERN_ERR + "ME4000:alloc_ai_context():Can't get memory for ai context\n"); + return -ENOMEM; + } + memset(ai_context, 0, sizeof(me4000_ai_context_t)); + + info->ai_context = ai_context; + + spin_lock_init(&ai_context->use_lock); + spin_lock_init(&ai_context->int_lock); + ai_context->number = 0; + ai_context->irq = info->irq; + init_waitqueue_head(&ai_context->wait_queue); + ai_context->board_info = info; + + ai_context->ctrl_reg = + info->me4000_regbase + ME4000_AI_CTRL_REG; + ai_context->status_reg = + info->me4000_regbase + ME4000_AI_STATUS_REG; + ai_context->channel_list_reg = + info->me4000_regbase + ME4000_AI_CHANNEL_LIST_REG; + ai_context->data_reg = + info->me4000_regbase + ME4000_AI_DATA_REG; + ai_context->chan_timer_reg = + info->me4000_regbase + ME4000_AI_CHAN_TIMER_REG; + ai_context->chan_pre_timer_reg = + info->me4000_regbase + ME4000_AI_CHAN_PRE_TIMER_REG; + ai_context->scan_timer_low_reg = + info->me4000_regbase + ME4000_AI_SCAN_TIMER_LOW_REG; + ai_context->scan_timer_high_reg = + info->me4000_regbase + ME4000_AI_SCAN_TIMER_HIGH_REG; + ai_context->scan_pre_timer_low_reg = + info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG; + ai_context->scan_pre_timer_high_reg = + info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG; + ai_context->start_reg = + info->me4000_regbase + ME4000_AI_START_REG; + ai_context->irq_status_reg = + info->me4000_regbase + ME4000_IRQ_STATUS_REG; + ai_context->sample_counter_reg = + info->me4000_regbase + ME4000_AI_SAMPLE_COUNTER_REG; + } + + return 0; +} + +static int alloc_dio_context(me4000_info_t * info) +{ + me4000_dio_context_t *dio_context; + + if (info->board_p->dio.count) { + dio_context = kmalloc(sizeof(me4000_dio_context_t), GFP_KERNEL); + if (!dio_context) { + printk(KERN_ERR + "ME4000:alloc_dio_context():Can't get memory for dio context\n"); + return -ENOMEM; + } + memset(dio_context, 0, sizeof(me4000_dio_context_t)); + + info->dio_context = dio_context; + + spin_lock_init(&dio_context->use_lock); + dio_context->board_info = info; + + dio_context->dio_count = info->board_p->dio.count; + + dio_context->dir_reg = + info->me4000_regbase + ME4000_DIO_DIR_REG; + dio_context->ctrl_reg = + info->me4000_regbase + ME4000_DIO_CTRL_REG; + dio_context->port_0_reg = + info->me4000_regbase + ME4000_DIO_PORT_0_REG; + dio_context->port_1_reg = + info->me4000_regbase + ME4000_DIO_PORT_1_REG; + dio_context->port_2_reg = + info->me4000_regbase + ME4000_DIO_PORT_2_REG; + dio_context->port_3_reg = + info->me4000_regbase + ME4000_DIO_PORT_3_REG; + } + + return 0; +} + +static int alloc_cnt_context(me4000_info_t * info) +{ + me4000_cnt_context_t *cnt_context; + + if (info->board_p->cnt.count) { + cnt_context = kmalloc(sizeof(me4000_cnt_context_t), GFP_KERNEL); + if (!cnt_context) { + printk(KERN_ERR + "ME4000:alloc_cnt_context():Can't get memory for cnt context\n"); + return -ENOMEM; + } + memset(cnt_context, 0, sizeof(me4000_cnt_context_t)); + + info->cnt_context = cnt_context; + + spin_lock_init(&cnt_context->use_lock); + cnt_context->board_info = info; + + cnt_context->ctrl_reg = + info->timer_regbase + ME4000_CNT_CTRL_REG; + cnt_context->counter_0_reg = + info->timer_regbase + ME4000_CNT_COUNTER_0_REG; + cnt_context->counter_1_reg = + info->timer_regbase + ME4000_CNT_COUNTER_1_REG; + cnt_context->counter_2_reg = + info->timer_regbase + ME4000_CNT_COUNTER_2_REG; + } + + return 0; +} + +static int alloc_ext_int_context(me4000_info_t * info) +{ + me4000_ext_int_context_t *ext_int_context; + + if (info->board_p->cnt.count) { + ext_int_context = + kmalloc(sizeof(me4000_ext_int_context_t), GFP_KERNEL); + if (!ext_int_context) { + printk(KERN_ERR + "ME4000:alloc_ext_int_context():Can't get memory for cnt context\n"); + return -ENOMEM; + } + memset(ext_int_context, 0, sizeof(me4000_ext_int_context_t)); + + info->ext_int_context = ext_int_context; + + spin_lock_init(&ext_int_context->use_lock); + ext_int_context->board_info = info; + + ext_int_context->fasync_ptr = NULL; + ext_int_context->irq = info->irq; + + ext_int_context->ctrl_reg = + info->me4000_regbase + ME4000_AI_CTRL_REG; + ext_int_context->irq_status_reg = + info->me4000_regbase + ME4000_IRQ_STATUS_REG; + } + + return 0; +} + +static int me4000_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int result = 0; + me4000_info_t *board_info; + + CALL_PDEBUG("me4000_probe() is executed\n"); + + /* Allocate structure for board context */ + board_info = kmalloc(sizeof(me4000_info_t), GFP_KERNEL); + if (!board_info) { + printk(KERN_ERR + "ME4000:Can't get memory for board info structure\n"); + result = -ENOMEM; + goto PROBE_ERROR_1; + } + memset(board_info, 0, sizeof(me4000_info_t)); + + /* Add to global linked list */ + list_add_tail(&board_info->list, &me4000_board_info_list); + + /* Get the PCI base registers */ + result = get_registers(dev, board_info); + if (result) { + printk(KERN_ERR "me4000_probe():Cannot get registers\n"); + goto PROBE_ERROR_2; + } + + /* Enable the device */ + result = pci_enable_device(dev); + if (result < 0) { + printk(KERN_ERR "me4000_probe():Cannot enable PCI device\n"); + goto PROBE_ERROR_2; + } + + /* Request the PCI register regions */ + result = pci_request_regions(dev, ME4000_NAME); + if (result < 0) { + printk(KERN_ERR "me4000_probe():Cannot request I/O regions\n"); + goto PROBE_ERROR_2; + } + + /* Initialize board info */ + result = init_board_info(dev, board_info); + if (result) { + printk(KERN_ERR "me4000_probe():Cannot init baord info\n"); + goto PROBE_ERROR_3; + } + + /* Download the xilinx firmware */ + result = me4000_xilinx_download(board_info); + if (result) { + printk(KERN_ERR "me4000_probe:Can't download firmware\n"); + goto PROBE_ERROR_3; + } + + /* Make a hardware reset */ + result = me4000_reset_board(board_info); + if (result) { + printk(KERN_ERR "me4000_probe:Can't reset board\n"); + goto PROBE_ERROR_3; + } + + /* Allocate analog output context structures */ + result = alloc_ao_contexts(board_info); + if (result) { + printk(KERN_ERR "me4000_probe():Cannot allocate ao contexts\n"); + goto PROBE_ERROR_3; + } + + /* Allocate analog input context */ + result = alloc_ai_context(board_info); + if (result) { + printk(KERN_ERR "me4000_probe():Cannot allocate ai context\n"); + goto PROBE_ERROR_4; + } + + /* Allocate digital I/O context */ + result = alloc_dio_context(board_info); + if (result) { + printk(KERN_ERR "me4000_probe():Cannot allocate dio context\n"); + goto PROBE_ERROR_5; + } + + /* Allocate counter context */ + result = alloc_cnt_context(board_info); + if (result) { + printk(KERN_ERR "me4000_probe():Cannot allocate cnt context\n"); + goto PROBE_ERROR_6; + } + + /* Allocate external interrupt context */ + result = alloc_ext_int_context(board_info); + if (result) { + printk(KERN_ERR + "me4000_probe():Cannot allocate ext_int context\n"); + goto PROBE_ERROR_7; + } + + return 0; + + PROBE_ERROR_7: + kfree(board_info->cnt_context); + + PROBE_ERROR_6: + kfree(board_info->dio_context); + + PROBE_ERROR_5: + kfree(board_info->ai_context); + + PROBE_ERROR_4: + release_ao_contexts(board_info); + + PROBE_ERROR_3: + pci_release_regions(dev); + + PROBE_ERROR_2: + list_del(&board_info->list); + kfree(board_info); + + PROBE_ERROR_1: + return result; +} + +static int me4000_xilinx_download(me4000_info_t * info) +{ + int size = 0; + u32 value = 0; + int idx = 0; + unsigned char *firm; + wait_queue_head_t queue; + + CALL_PDEBUG("me4000_xilinx_download() is executed\n"); + + init_waitqueue_head(&queue); + + firm = (info->device_id == 0x4610) ? xilinx_firm_4610 : xilinx_firm; + + /* + * Set PLX local interrupt 2 polarity to high. + * Interrupt is thrown by init pin of xilinx. + */ + outl(0x10, info->plx_regbase + PLX_INTCSR); + + /* Set /CS and /WRITE of the Xilinx */ + value = inl(info->plx_regbase + PLX_ICR); + value |= 0x100; + outl(value, info->plx_regbase + PLX_ICR); + + /* Init Xilinx with CS1 */ + inb(info->program_regbase + 0xC8); + + /* Wait until /INIT pin is set */ + udelay(20); + if (!inl(info->plx_regbase + PLX_INTCSR) & 0x20) { + printk(KERN_ERR "me4000_xilinx_download():Can't init Xilinx\n"); + return -EIO; + } + + /* Reset /CS and /WRITE of the Xilinx */ + value = inl(info->plx_regbase + PLX_ICR); + value &= ~0x100; + outl(value, info->plx_regbase + PLX_ICR); + + /* Download Xilinx firmware */ + size = (firm[0] << 24) + (firm[1] << 16) + (firm[2] << 8) + firm[3]; + udelay(10); + + for (idx = 0; idx < size; idx++) { + outb(firm[16 + idx], info->program_regbase); + + udelay(10); + + /* Check if BUSY flag is low */ + if (inl(info->plx_regbase + PLX_ICR) & 0x20) { + printk(KERN_ERR + "me4000_xilinx_download():Xilinx is still busy (idx = %d)\n", + idx); + return -EIO; + } + } + + PDEBUG("me4000_xilinx_download():%d bytes written\n", idx); + + /* If done flag is high download was successful */ + if (inl(info->plx_regbase + PLX_ICR) & 0x4) { + PDEBUG("me4000_xilinx_download():Done flag is set\n"); + PDEBUG("me4000_xilinx_download():Download was successful\n"); + } else { + printk(KERN_ERR + "ME4000:me4000_xilinx_download():DONE flag is not set\n"); + printk(KERN_ERR + "ME4000:me4000_xilinx_download():Download not succesful\n"); + return -EIO; + } + + /* Set /CS and /WRITE */ + value = inl(info->plx_regbase + PLX_ICR); + value |= 0x100; + outl(value, info->plx_regbase + PLX_ICR); + + return 0; +} + +static int me4000_reset_board(me4000_info_t * info) +{ + unsigned long icr; + + CALL_PDEBUG("me4000_reset_board() is executed\n"); + + /* Make a hardware reset */ + icr = me4000_inl(info->plx_regbase + PLX_ICR); + icr |= 0x40000000; + me4000_outl(icr, info->plx_regbase + PLX_ICR); + icr &= ~0x40000000; + me4000_outl(icr, info->plx_regbase + PLX_ICR); + + /* Set both stop bits in the analog input control register */ + me4000_outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP, + info->me4000_regbase + ME4000_AI_CTRL_REG); + + /* Set both stop bits in the analog output control register */ + me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, + info->me4000_regbase + ME4000_AO_00_CTRL_REG); + me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, + info->me4000_regbase + ME4000_AO_01_CTRL_REG); + me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, + info->me4000_regbase + ME4000_AO_02_CTRL_REG); + me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP, + info->me4000_regbase + ME4000_AO_03_CTRL_REG); + + /* 0x8000 to the DACs means an output voltage of 0V */ + me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_00_SINGLE_REG); + me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_01_SINGLE_REG); + me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_02_SINGLE_REG); + me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_03_SINGLE_REG); + + /* Enable interrupts on the PLX */ + me4000_outl(0x43, info->plx_regbase + PLX_INTCSR); + + /* Set the adustment register for AO demux */ + me4000_outl(ME4000_AO_DEMUX_ADJUST_VALUE, + info->me4000_regbase + ME4000_AO_DEMUX_ADJUST_REG); + + /* Set digital I/O direction for port 0 to output on isolated versions */ + if (!(me4000_inl(info->me4000_regbase + ME4000_DIO_DIR_REG) & 0x1)) { + me4000_outl(0x1, info->me4000_regbase + ME4000_DIO_CTRL_REG); + } + + return 0; +} + +static int me4000_open(struct inode *inode_p, struct file *file_p) +{ + int board, dev, mode; + int err = 0; + int i; + struct list_head *ptr; + me4000_info_t *board_info = NULL; + me4000_ao_context_t *ao_context = NULL; + me4000_ai_context_t *ai_context = NULL; + me4000_dio_context_t *dio_context = NULL; + me4000_cnt_context_t *cnt_context = NULL; + me4000_ext_int_context_t *ext_int_context = NULL; + + CALL_PDEBUG("me4000_open() is executed\n"); + + /* Analog output */ + if (MAJOR(inode_p->i_rdev) == me4000_ao_major_driver_no) { + board = AO_BOARD(inode_p->i_rdev); + dev = AO_PORT(inode_p->i_rdev); + mode = AO_MODE(inode_p->i_rdev); + + PDEBUG("me4000_open():board = %d ao = %d mode = %d\n", board, + dev, mode); + + /* Search for the board context */ + for (ptr = me4000_board_info_list.next, i = 0; + ptr != &me4000_board_info_list; ptr = ptr->next, i++) { + board_info = list_entry(ptr, me4000_info_t, list); + if (i == board) + break; + } + + if (ptr == &me4000_board_info_list) { + printk(KERN_ERR + "ME4000:me4000_open():Board %d not in device list\n", + board); + return -ENODEV; + } + + /* Search for the dac context */ + for (ptr = board_info->ao_context_list.next, i = 0; + ptr != &board_info->ao_context_list; + ptr = ptr->next, i++) { + ao_context = list_entry(ptr, me4000_ao_context_t, list); + if (i == dev) + break; + } + + if (ptr == &board_info->ao_context_list) { + printk(KERN_ERR + "ME4000:me4000_open():Device %d not in device list\n", + dev); + return -ENODEV; + } + + /* Check if mode is valid */ + if (mode > 2) { + printk(KERN_ERR + "ME4000:me4000_open():Mode is not valid\n"); + return -ENODEV; + } + + /* Check if mode is valid for this AO */ + if ((mode != ME4000_AO_CONV_MODE_SINGLE) + && (dev >= board_info->board_p->ao.fifo_count)) { + printk(KERN_ERR + "ME4000:me4000_open():AO %d only in single mode available\n", + dev); + return -ENODEV; + } + + /* Check if already opened */ + spin_lock(&ao_context->use_lock); + if (ao_context->dac_in_use) { + printk(KERN_ERR + "ME4000:me4000_open():AO %d already in use\n", + dev); + spin_unlock(&ao_context->use_lock); + return -EBUSY; + } + ao_context->dac_in_use = 1; + spin_unlock(&ao_context->use_lock); + + ao_context->mode = mode; + + /* Hold the context in private data */ + file_p->private_data = ao_context; + + /* Set file operations pointer */ + file_p->f_op = me4000_ao_fops_array[mode]; + + err = me4000_ao_prepare(ao_context); + if (err) { + ao_context->dac_in_use = 0; + return 1; + } + } + /* Analog input */ + else if (MAJOR(inode_p->i_rdev) == me4000_ai_major_driver_no) { + board = AI_BOARD(inode_p->i_rdev); + mode = AI_MODE(inode_p->i_rdev); + + PDEBUG("me4000_open():ai board = %d mode = %d\n", board, mode); + + /* Search for the board context */ + for (ptr = me4000_board_info_list.next, i = 0; + ptr != &me4000_board_info_list; ptr = ptr->next, i++) { + board_info = list_entry(ptr, me4000_info_t, list); + if (i == board) + break; + } + + if (ptr == &me4000_board_info_list) { + printk(KERN_ERR + "ME4000:me4000_open():Board %d not in device list\n", + board); + return -ENODEV; + } + + ai_context = board_info->ai_context; + + /* Check if mode is valid */ + if (mode > 5) { + printk(KERN_ERR + "ME4000:me4000_open():Mode is not valid\n"); + return -EINVAL; + } + + /* Check if already opened */ + spin_lock(&ai_context->use_lock); + if (ai_context->in_use) { + printk(KERN_ERR + "ME4000:me4000_open():AI already in use\n"); + spin_unlock(&ai_context->use_lock); + return -EBUSY; + } + ai_context->in_use = 1; + spin_unlock(&ai_context->use_lock); + + ai_context->mode = mode; + + /* Hold the context in private data */ + file_p->private_data = ai_context; + + /* Set file operations pointer */ + file_p->f_op = me4000_ai_fops_array[mode]; + + /* Prepare analog input */ + me4000_ai_prepare(ai_context); + } + /* Digital I/O */ + else if (MAJOR(inode_p->i_rdev) == me4000_dio_major_driver_no) { + board = DIO_BOARD(inode_p->i_rdev); + dev = 0; + mode = 0; + + PDEBUG("me4000_open():board = %d\n", board); + + /* Search for the board context */ + for (ptr = me4000_board_info_list.next; + ptr != &me4000_board_info_list; ptr = ptr->next) { + board_info = list_entry(ptr, me4000_info_t, list); + if (board_info->board_count == board) + break; + } + + if (ptr == &me4000_board_info_list) { + printk(KERN_ERR + "ME4000:me4000_open():Board %d not in device list\n", + board); + return -ENODEV; + } + + /* Search for the dio context */ + dio_context = board_info->dio_context; + + /* Check if already opened */ + spin_lock(&dio_context->use_lock); + if (dio_context->in_use) { + printk(KERN_ERR + "ME4000:me4000_open():DIO already in use\n"); + spin_unlock(&dio_context->use_lock); + return -EBUSY; + } + dio_context->in_use = 1; + spin_unlock(&dio_context->use_lock); + + /* Hold the context in private data */ + file_p->private_data = dio_context; + + /* Set file operations pointer to single functions */ + file_p->f_op = &me4000_dio_fops; + + //me4000_dio_reset(dio_context); + } + /* Counters */ + else if (MAJOR(inode_p->i_rdev) == me4000_cnt_major_driver_no) { + board = CNT_BOARD(inode_p->i_rdev); + dev = 0; + mode = 0; + + PDEBUG("me4000_open():board = %d\n", board); + + /* Search for the board context */ + for (ptr = me4000_board_info_list.next; + ptr != &me4000_board_info_list; ptr = ptr->next) { + board_info = list_entry(ptr, me4000_info_t, list); + if (board_info->board_count == board) + break; + } + + if (ptr == &me4000_board_info_list) { + printk(KERN_ERR + "ME4000:me4000_open():Board %d not in device list\n", + board); + return -ENODEV; + } + + /* Get the cnt context */ + cnt_context = board_info->cnt_context; + + /* Check if already opened */ + spin_lock(&cnt_context->use_lock); + if (cnt_context->in_use) { + printk(KERN_ERR + "ME4000:me4000_open():CNT already in use\n"); + spin_unlock(&cnt_context->use_lock); + return -EBUSY; + } + cnt_context->in_use = 1; + spin_unlock(&cnt_context->use_lock); + + /* Hold the context in private data */ + file_p->private_data = cnt_context; + + /* Set file operations pointer to single functions */ + file_p->f_op = &me4000_cnt_fops; + } + /* External Interrupt */ + else if (MAJOR(inode_p->i_rdev) == me4000_ext_int_major_driver_no) { + board = EXT_INT_BOARD(inode_p->i_rdev); + dev = 0; + mode = 0; + + PDEBUG("me4000_open():board = %d\n", board); + + /* Search for the board context */ + for (ptr = me4000_board_info_list.next; + ptr != &me4000_board_info_list; ptr = ptr->next) { + board_info = list_entry(ptr, me4000_info_t, list); + if (board_info->board_count == board) + break; + } + + if (ptr == &me4000_board_info_list) { + printk(KERN_ERR + "ME4000:me4000_open():Board %d not in device list\n", + board); + return -ENODEV; + } + + /* Get the external interrupt context */ + ext_int_context = board_info->ext_int_context; + + /* Check if already opened */ + spin_lock(&cnt_context->use_lock); + if (ext_int_context->in_use) { + printk(KERN_ERR + "ME4000:me4000_open():External interrupt already in use\n"); + spin_unlock(&ext_int_context->use_lock); + return -EBUSY; + } + ext_int_context->in_use = 1; + spin_unlock(&ext_int_context->use_lock); + + /* Hold the context in private data */ + file_p->private_data = ext_int_context; + + /* Set file operations pointer to single functions */ + file_p->f_op = &me4000_ext_int_fops; + + /* Request the interrupt line */ + err = + request_irq(ext_int_context->irq, me4000_ext_int_isr, + IRQF_DISABLED | IRQF_SHARED, ME4000_NAME, + ext_int_context); + if (err) { + printk(KERN_ERR + "ME4000:me4000_open():Can't get interrupt line"); + ext_int_context->in_use = 0; + return -ENODEV; + } + + /* Reset the counter */ + me4000_ext_int_disable(ext_int_context); + } else { + printk(KERN_ERR "ME4000:me4000_open():Major number unknown\n"); + return -EINVAL; + } + + return 0; +} + +static int me4000_release(struct inode *inode_p, struct file *file_p) +{ + me4000_ao_context_t *ao_context; + me4000_ai_context_t *ai_context; + me4000_dio_context_t *dio_context; + me4000_cnt_context_t *cnt_context; + me4000_ext_int_context_t *ext_int_context; + + CALL_PDEBUG("me4000_release() is executed\n"); + + if (MAJOR(inode_p->i_rdev) == me4000_ao_major_driver_no) { + ao_context = file_p->private_data; + + /* Mark DAC as unused */ + ao_context->dac_in_use = 0; + } else if (MAJOR(inode_p->i_rdev) == me4000_ai_major_driver_no) { + ai_context = file_p->private_data; + + /* Reset the analog input */ + me4000_ai_reset(ai_context); + + /* Free the interrupt and the circular buffer */ + if (ai_context->mode) { + free_irq(ai_context->irq, ai_context); + kfree(ai_context->circ_buf.buf); + ai_context->circ_buf.buf = NULL; + ai_context->circ_buf.head = 0; + ai_context->circ_buf.tail = 0; + } + + /* Mark AI as unused */ + ai_context->in_use = 0; + } else if (MAJOR(inode_p->i_rdev) == me4000_dio_major_driver_no) { + dio_context = file_p->private_data; + + /* Mark digital I/O as unused */ + dio_context->in_use = 0; + } else if (MAJOR(inode_p->i_rdev) == me4000_cnt_major_driver_no) { + cnt_context = file_p->private_data; + + /* Mark counters as unused */ + cnt_context->in_use = 0; + } else if (MAJOR(inode_p->i_rdev) == me4000_ext_int_major_driver_no) { + ext_int_context = file_p->private_data; + + /* Disable the externel interrupt */ + me4000_ext_int_disable(ext_int_context); + + free_irq(ext_int_context->irq, ext_int_context); + + /* Delete the fasync structure and free memory */ + me4000_ext_int_fasync(0, file_p, 0); + + /* Mark as unused */ + ext_int_context->in_use = 0; + } else { + printk(KERN_ERR + "ME4000:me4000_release():Major number unknown\n"); + return -EINVAL; + } + + return 0; +} + +/*------------------------------- Analog output stuff --------------------------------------*/ + +static int me4000_ao_prepare(me4000_ao_context_t * ao_context) +{ + unsigned long flags; + + CALL_PDEBUG("me4000_ao_prepare() is executed\n"); + + if (ao_context->mode == ME4000_AO_CONV_MODE_CONTINUOUS) { + /* Only do anything if not already in the correct mode */ + unsigned long mode = me4000_inl(ao_context->ctrl_reg); + if ((mode & ME4000_AO_CONV_MODE_CONTINUOUS) + && (mode & ME4000_AO_CTRL_BIT_ENABLE_FIFO)) { + return 0; + } + + /* Stop any conversion */ + me4000_ao_immediate_stop(ao_context); + + /* Set the control register to default state */ + spin_lock_irqsave(&ao_context->int_lock, flags); + me4000_outl(ME4000_AO_CONV_MODE_CONTINUOUS | + ME4000_AO_CTRL_BIT_ENABLE_FIFO | + ME4000_AO_CTRL_BIT_STOP | + ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, + ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + /* Set to fastest sample rate */ + me4000_outl(65, ao_context->timer_reg); + } else if (ao_context->mode == ME4000_AO_CONV_MODE_WRAPAROUND) { + /* Only do anything if not already in the correct mode */ + unsigned long mode = me4000_inl(ao_context->ctrl_reg); + if ((mode & ME4000_AO_CONV_MODE_WRAPAROUND) + && (mode & ME4000_AO_CTRL_BIT_ENABLE_FIFO)) { + return 0; + } + + /* Stop any conversion */ + me4000_ao_immediate_stop(ao_context); + + /* Set the control register to default state */ + spin_lock_irqsave(&ao_context->int_lock, flags); + me4000_outl(ME4000_AO_CONV_MODE_WRAPAROUND | + ME4000_AO_CTRL_BIT_ENABLE_FIFO | + ME4000_AO_CTRL_BIT_STOP | + ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, + ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + /* Set to fastest sample rate */ + me4000_outl(65, ao_context->timer_reg); + } else if (ao_context->mode == ME4000_AO_CONV_MODE_SINGLE) { + /* Only do anything if not already in the correct mode */ + unsigned long mode = me4000_inl(ao_context->ctrl_reg); + if (! + (mode & + (ME4000_AO_CONV_MODE_WRAPAROUND | + ME4000_AO_CONV_MODE_CONTINUOUS))) { + return 0; + } + + /* Stop any conversion */ + me4000_ao_immediate_stop(ao_context); + + /* Clear the control register */ + spin_lock_irqsave(&ao_context->int_lock, flags); + me4000_outl(0x0, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + /* Set voltage to 0V */ + me4000_outl(0x8000, ao_context->single_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_ao_prepare():Invalid mode specified\n"); + return -EINVAL; + } + + return 0; +} + +static int me4000_ao_reset(me4000_ao_context_t * ao_context) +{ + u32 tmp; + wait_queue_head_t queue; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_reset() is executed\n"); + + init_waitqueue_head(&queue); + + if (ao_context->mode == ME4000_AO_CONV_MODE_WRAPAROUND) { + /* + * First stop conversion of the DAC before reconfigure. + * This is essantial, cause of the state machine. + * If not stopped before configuring mode, it could + * walk in a undefined state. + */ + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP; + me4000_outl(tmp, ao_context->ctrl_reg); + + while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { + sleep_on_timeout(&queue, 1); + } + + /* Set to transparent mode */ + me4000_ao_simultaneous_disable(ao_context); + + /* Set to single mode in order to set default voltage */ + me4000_outl(0x0, ao_context->ctrl_reg); + + /* Set voltage to 0V */ + me4000_outl(0x8000, ao_context->single_reg); + + /* Set to fastest sample rate */ + me4000_outl(65, ao_context->timer_reg); + + /* Set the original mode and enable FIFO */ + me4000_outl(ME4000_AO_CONV_MODE_WRAPAROUND | + ME4000_AO_CTRL_BIT_ENABLE_FIFO | + ME4000_AO_CTRL_BIT_STOP | + ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, + ao_context->ctrl_reg); + } else if (ao_context->mode == ME4000_AO_CONV_MODE_CONTINUOUS) { + /* + * First stop conversion of the DAC before reconfigure. + * This is essantial, cause of the state machine. + * If not stopped before configuring mode, it could + * walk in a undefined state. + */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_STOP; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { + sleep_on_timeout(&queue, 1); + } + + /* Clear the circular buffer */ + ao_context->circ_buf.head = 0; + ao_context->circ_buf.tail = 0; + + /* Set to transparent mode */ + me4000_ao_simultaneous_disable(ao_context); + + /* Set to single mode in order to set default voltage */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + me4000_outl(0x0, ao_context->ctrl_reg); + + /* Set voltage to 0V */ + me4000_outl(0x8000, ao_context->single_reg); + + /* Set to fastest sample rate */ + me4000_outl(65, ao_context->timer_reg); + + /* Set the original mode and enable FIFO */ + me4000_outl(ME4000_AO_CONV_MODE_CONTINUOUS | + ME4000_AO_CTRL_BIT_ENABLE_FIFO | + ME4000_AO_CTRL_BIT_STOP | + ME4000_AO_CTRL_BIT_IMMEDIATE_STOP, + ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + } else { + /* Set to transparent mode */ + me4000_ao_simultaneous_disable(ao_context); + + /* Set voltage to 0V */ + me4000_outl(0x8000, ao_context->single_reg); + } + + return 0; +} + +static ssize_t me4000_ao_write_sing(struct file *filep, const char *buff, + size_t cnt, loff_t * offp) +{ + me4000_ao_context_t *ao_context = filep->private_data; + u32 value; + const u16 *buffer = (const u16 *)buff; + + CALL_PDEBUG("me4000_ao_write_sing() is executed\n"); + + if (cnt != 2) { + printk(KERN_ERR + "me4000_ao_write_sing():Write count is not 2\n"); + return -EINVAL; + } + + if (get_user(value, buffer)) { + printk(KERN_ERR + "me4000_ao_write_sing():Cannot copy data from user\n"); + return -EFAULT; + } + + me4000_outl(value, ao_context->single_reg); + + return 2; +} + +static ssize_t me4000_ao_write_wrap(struct file *filep, const char *buff, + size_t cnt, loff_t * offp) +{ + me4000_ao_context_t *ao_context = filep->private_data; + size_t i; + u32 value; + u32 tmp; + const u16 *buffer = (const u16 *)buff; + size_t count = cnt / 2; + + CALL_PDEBUG("me4000_ao_write_wrap() is executed\n"); + + /* Check if a conversion is already running */ + if (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "ME4000:me4000_ao_write_wrap():There is already a conversion running\n"); + return -EBUSY; + } + + if (count > ME4000_AO_FIFO_COUNT) { + printk(KERN_ERR + "me4000_ao_write_wrap():Can't load more than %d values\n", + ME4000_AO_FIFO_COUNT); + return -ENOSPC; + } + + /* Reset the FIFO */ + tmp = inl(ao_context->ctrl_reg); + tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_FIFO; + outl(tmp, ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_ENABLE_FIFO; + outl(tmp, ao_context->ctrl_reg); + + for (i = 0; i < count; i++) { + if (get_user(value, buffer + i)) { + printk(KERN_ERR + "me4000_ao_write_single():Cannot copy data from user\n"); + return -EFAULT; + } + if (((ao_context->fifo_reg & 0xFF) == ME4000_AO_01_FIFO_REG) + || ((ao_context->fifo_reg & 0xFF) == ME4000_AO_03_FIFO_REG)) + value = value << 16; + outl(value, ao_context->fifo_reg); + } + CALL_PDEBUG("me4000_ao_write_wrap() is leaved with %d\n", i * 2); + + return i * 2; +} + +static ssize_t me4000_ao_write_cont(struct file *filep, const char *buff, + size_t cnt, loff_t * offp) +{ + me4000_ao_context_t *ao_context = filep->private_data; + const u16 *buffer = (const u16 *)buff; + size_t count = cnt / 2; + unsigned long flags; + u32 tmp; + int c = 0; + int k = 0; + int ret = 0; + u16 svalue; + u32 lvalue; + int i; + wait_queue_head_t queue; + + CALL_PDEBUG("me4000_ao_write_cont() is executed\n"); + + init_waitqueue_head(&queue); + + /* Check count */ + if (count <= 0) { + PDEBUG("me4000_ao_write_cont():Count is 0\n"); + return 0; + } + + if (filep->f_flags & O_APPEND) { + PDEBUG("me4000_ao_write_cont():Append data to data stream\n"); + while (count > 0) { + if (filep->f_flags & O_NONBLOCK) { + if (ao_context->pipe_flag) { + printk(KERN_ERR + "ME4000:me4000_ao_write_cont():Broken pipe in nonblocking write\n"); + return -EPIPE; + } + c = me4000_space_to_end(ao_context->circ_buf, + ME4000_AO_BUFFER_COUNT); + if (!c) { + PDEBUG + ("me4000_ao_write_cont():Returning from nonblocking write\n"); + break; + } + } else { + wait_event_interruptible(ao_context->wait_queue, + (c = + me4000_space_to_end + (ao_context->circ_buf, + ME4000_AO_BUFFER_COUNT))); + if (ao_context->pipe_flag) { + printk(KERN_ERR + "me4000_ao_write_cont():Broken pipe in blocking write\n"); + return -EPIPE; + } + if (signal_pending(current)) { + printk(KERN_ERR + "me4000_ao_write_cont():Wait for free buffer interrupted from signal\n"); + return -EINTR; + } + } + + PDEBUG("me4000_ao_write_cont():Space to end = %d\n", c); + + /* Only able to write size of free buffer or size of count */ + if (count < c) + c = count; + + k = 2 * c; + k -= copy_from_user(ao_context->circ_buf.buf + + ao_context->circ_buf.head, buffer, + k); + c = k / 2; + PDEBUG + ("me4000_ao_write_cont():Copy %d values from user space\n", + c); + + if (!c) + return -EFAULT; + + ao_context->circ_buf.head = + (ao_context->circ_buf.head + + c) & (ME4000_AO_BUFFER_COUNT - 1); + buffer += c; + count -= c; + ret += c; + + /* Values are now available so enable interrupts */ + spin_lock_irqsave(&ao_context->int_lock, flags); + if (me4000_buf_count + (ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) { + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_ENABLE_IRQ; + me4000_outl(tmp, ao_context->ctrl_reg); + } + spin_unlock_irqrestore(&ao_context->int_lock, flags); + } + + /* Wait until the state machine is stopped if O_SYNC is set */ + if (filep->f_flags & O_SYNC) { + while (inl(ao_context->status_reg) & + ME4000_AO_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (ao_context->pipe_flag) { + PDEBUG + ("me4000_ao_write_cont():Broken pipe detected after sync\n"); + return -EPIPE; + } + if (signal_pending(current)) { + printk(KERN_ERR + "me4000_ao_write_cont():Wait on state machine after sync interrupted\n"); + return -EINTR; + } + } + } + } else { + PDEBUG("me4000_ao_write_cont():Preload DAC FIFO\n"); + if ((me4000_inl(ao_context->status_reg) & + ME4000_AO_STATUS_BIT_FSM)) { + printk(KERN_ERR + "me4000_ao_write_cont():Can't Preload DAC FIFO while conversion is running\n"); + return -EBUSY; + } + + /* Clear the FIFO */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp &= + ~(ME4000_AO_CTRL_BIT_ENABLE_FIFO | + ME4000_AO_CTRL_BIT_ENABLE_IRQ); + me4000_outl(tmp, ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_ENABLE_FIFO; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + /* Clear the circular buffer */ + ao_context->circ_buf.head = 0; + ao_context->circ_buf.tail = 0; + + /* Reset the broken pipe flag */ + ao_context->pipe_flag = 0; + + /* Only able to write size of fifo or count */ + c = ME4000_AO_FIFO_COUNT; + if (count < c) + c = count; + + PDEBUG + ("me4000_ao_write_cont():Write %d values to DAC on 0x%lX\n", + c, ao_context->fifo_reg); + + /* Write values to the fifo */ + for (i = 0; i < c; i++) { + if (get_user(svalue, buffer)) + return -EFAULT; + + if (((ao_context->fifo_reg & 0xFF) == + ME4000_AO_01_FIFO_REG) + || ((ao_context->fifo_reg & 0xFF) == + ME4000_AO_03_FIFO_REG)) { + lvalue = ((u32) svalue) << 16; + } else + lvalue = (u32) svalue; + + outl(lvalue, ao_context->fifo_reg); + buffer++; + } + count -= c; + ret += c; + + while (1) { + /* Get free buffer */ + c = me4000_space_to_end(ao_context->circ_buf, + ME4000_AO_BUFFER_COUNT); + + if (c == 0) + return (2 * ret); + + /* Only able to write size of free buffer or size of count */ + if (count < c) + c = count; + + /* If count = 0 return to user */ + if (c <= 0) { + PDEBUG + ("me4000_ao_write_cont():Count reached 0\n"); + break; + } + + k = 2 * c; + k -= copy_from_user(ao_context->circ_buf.buf + + ao_context->circ_buf.head, buffer, + k); + c = k / 2; + PDEBUG + ("me4000_ao_write_cont():Wrote %d values to buffer\n", + c); + + if (!c) + return -EFAULT; + + ao_context->circ_buf.head = + (ao_context->circ_buf.head + + c) & (ME4000_AO_BUFFER_COUNT - 1); + buffer += c; + count -= c; + ret += c; + + /* If values in the buffer are available so enable interrupts */ + spin_lock_irqsave(&ao_context->int_lock, flags); + if (me4000_buf_count + (ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) { + PDEBUG + ("me4000_ao_write_cont():Enable Interrupts\n"); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_ENABLE_IRQ; + me4000_outl(tmp, ao_context->ctrl_reg); + } + spin_unlock_irqrestore(&ao_context->int_lock, flags); + } + } + + if (filep->f_flags & O_NONBLOCK) { + return (ret == 0) ? -EAGAIN : 2 * ret; + } + + return 2 * ret; +} + +static unsigned int me4000_ao_poll_cont(struct file *file_p, poll_table * wait) +{ + me4000_ao_context_t *ao_context; + unsigned long mask = 0; + + CALL_PDEBUG("me4000_ao_poll_cont() is executed\n"); + + ao_context = file_p->private_data; + + poll_wait(file_p, &ao_context->wait_queue, wait); + + /* Get free buffer */ + if (me4000_space_to_end(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) + mask |= POLLOUT | POLLWRNORM; + + CALL_PDEBUG("me4000_ao_poll_cont():Return mask %lX\n", mask); + + return mask; +} + +static int me4000_ao_fsync_cont(struct file *file_p, struct dentry *dentry_p, + int datasync) +{ + me4000_ao_context_t *ao_context; + wait_queue_head_t queue; + + CALL_PDEBUG("me4000_ao_fsync_cont() is executed\n"); + + ao_context = file_p->private_data; + init_waitqueue_head(&queue); + + while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (ao_context->pipe_flag) { + printk(KERN_ERR + "me4000_ao_fsync_cont():Broken pipe detected\n"); + return -EPIPE; + } + + if (signal_pending(current)) { + printk(KERN_ERR + "me4000_ao_fsync_cont():Wait on state machine interrupted\n"); + return -EINTR; + } + } + + return 0; +} + +static int me4000_ao_ioctl_sing(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ao_context_t *ao_context; + + CALL_PDEBUG("me4000_ao_ioctl_sing() is executed\n"); + + ao_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + return -ENOTTY; + PDEBUG("me4000_ao_ioctl_sing():Wrong magic number\n"); + } + + switch (service) { + case ME4000_AO_EX_TRIG_SETUP: + return me4000_ao_ex_trig_set_edge((int *)arg, ao_context); + case ME4000_AO_EX_TRIG_ENABLE: + return me4000_ao_ex_trig_enable(ao_context); + case ME4000_AO_EX_TRIG_DISABLE: + return me4000_ao_ex_trig_disable(ao_context); + case ME4000_AO_PRELOAD: + return me4000_ao_preload(ao_context); + case ME4000_AO_PRELOAD_UPDATE: + return me4000_ao_preload_update(ao_context); + case ME4000_GET_USER_INFO: + return me4000_get_user_info((me4000_user_info_t *) arg, + ao_context->board_info); + case ME4000_AO_SIMULTANEOUS_EX_TRIG: + return me4000_ao_simultaneous_ex_trig(ao_context); + case ME4000_AO_SIMULTANEOUS_SW: + return me4000_ao_simultaneous_sw(ao_context); + case ME4000_AO_SIMULTANEOUS_DISABLE: + return me4000_ao_simultaneous_disable(ao_context); + case ME4000_AO_SIMULTANEOUS_UPDATE: + return + me4000_ao_simultaneous_update((me4000_ao_channel_list_t *) + arg, ao_context); + case ME4000_AO_EX_TRIG_TIMEOUT: + return me4000_ao_ex_trig_timeout((unsigned long *)arg, + ao_context); + case ME4000_AO_DISABLE_DO: + return me4000_ao_disable_do(ao_context); + default: + printk(KERN_ERR + "me4000_ao_ioctl_sing():Service number invalid\n"); + return -ENOTTY; + } + + return 0; +} + +static int me4000_ao_ioctl_wrap(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ao_context_t *ao_context; + + CALL_PDEBUG("me4000_ao_ioctl_wrap() is executed\n"); + + ao_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + return -ENOTTY; + PDEBUG("me4000_ao_ioctl_wrap():Wrong magic number\n"); + } + + switch (service) { + case ME4000_AO_START: + return me4000_ao_start((unsigned long *)arg, ao_context); + case ME4000_AO_STOP: + return me4000_ao_stop(ao_context); + case ME4000_AO_IMMEDIATE_STOP: + return me4000_ao_immediate_stop(ao_context); + case ME4000_AO_RESET: + return me4000_ao_reset(ao_context); + case ME4000_AO_TIMER_SET_DIVISOR: + return me4000_ao_timer_set_divisor((u32 *) arg, ao_context); + case ME4000_AO_EX_TRIG_SETUP: + return me4000_ao_ex_trig_set_edge((int *)arg, ao_context); + case ME4000_AO_EX_TRIG_ENABLE: + return me4000_ao_ex_trig_enable(ao_context); + case ME4000_AO_EX_TRIG_DISABLE: + return me4000_ao_ex_trig_disable(ao_context); + case ME4000_GET_USER_INFO: + return me4000_get_user_info((me4000_user_info_t *) arg, + ao_context->board_info); + case ME4000_AO_FSM_STATE: + return me4000_ao_fsm_state((int *)arg, ao_context); + case ME4000_AO_ENABLE_DO: + return me4000_ao_enable_do(ao_context); + case ME4000_AO_DISABLE_DO: + return me4000_ao_disable_do(ao_context); + case ME4000_AO_SYNCHRONOUS_EX_TRIG: + return me4000_ao_synchronous_ex_trig(ao_context); + case ME4000_AO_SYNCHRONOUS_SW: + return me4000_ao_synchronous_sw(ao_context); + case ME4000_AO_SYNCHRONOUS_DISABLE: + return me4000_ao_synchronous_disable(ao_context); + default: + return -ENOTTY; + } + return 0; +} + +static int me4000_ao_ioctl_cont(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ao_context_t *ao_context; + + CALL_PDEBUG("me4000_ao_ioctl_cont() is executed\n"); + + ao_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + return -ENOTTY; + PDEBUG("me4000_ao_ioctl_cont():Wrong magic number\n"); + } + + switch (service) { + case ME4000_AO_START: + return me4000_ao_start((unsigned long *)arg, ao_context); + case ME4000_AO_STOP: + return me4000_ao_stop(ao_context); + case ME4000_AO_IMMEDIATE_STOP: + return me4000_ao_immediate_stop(ao_context); + case ME4000_AO_RESET: + return me4000_ao_reset(ao_context); + case ME4000_AO_TIMER_SET_DIVISOR: + return me4000_ao_timer_set_divisor((u32 *) arg, ao_context); + case ME4000_AO_EX_TRIG_SETUP: + return me4000_ao_ex_trig_set_edge((int *)arg, ao_context); + case ME4000_AO_EX_TRIG_ENABLE: + return me4000_ao_ex_trig_enable(ao_context); + case ME4000_AO_EX_TRIG_DISABLE: + return me4000_ao_ex_trig_disable(ao_context); + case ME4000_AO_ENABLE_DO: + return me4000_ao_enable_do(ao_context); + case ME4000_AO_DISABLE_DO: + return me4000_ao_disable_do(ao_context); + case ME4000_AO_FSM_STATE: + return me4000_ao_fsm_state((int *)arg, ao_context); + case ME4000_GET_USER_INFO: + return me4000_get_user_info((me4000_user_info_t *) arg, + ao_context->board_info); + case ME4000_AO_SYNCHRONOUS_EX_TRIG: + return me4000_ao_synchronous_ex_trig(ao_context); + case ME4000_AO_SYNCHRONOUS_SW: + return me4000_ao_synchronous_sw(ao_context); + case ME4000_AO_SYNCHRONOUS_DISABLE: + return me4000_ao_synchronous_disable(ao_context); + case ME4000_AO_GET_FREE_BUFFER: + return me4000_ao_get_free_buffer((unsigned long *)arg, + ao_context); + default: + return -ENOTTY; + } + return 0; +} + +static int me4000_ao_start(unsigned long *arg, me4000_ao_context_t * ao_context) +{ + u32 tmp; + wait_queue_head_t queue; + unsigned long ref; + unsigned long timeout; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_start() is executed\n"); + + if (get_user(timeout, arg)) { + printk(KERN_ERR + "me4000_ao_start():Cannot copy data from user\n"); + return -EFAULT; + } + + init_waitqueue_head(&queue); + + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = inl(ao_context->ctrl_reg); + tmp &= ~(ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + if ((tmp & ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG)) { + if (timeout) { + ref = jiffies; + while (! + (inl(ao_context->status_reg) & + ME4000_AO_STATUS_BIT_FSM)) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ao_start():Wait on start of state machine interrupted\n"); + return -EINTR; + } + if (((jiffies - ref) > (timeout * HZ / USER_HZ))) { // 2.6 has diffrent definitions for HZ in user and kernel space + printk(KERN_ERR + "ME4000:me4000_ao_start():Timeout reached\n"); + return -EIO; + } + } + } + } else { + me4000_outl(0x8000, ao_context->single_reg); + } + + return 0; +} + +static int me4000_ao_stop(me4000_ao_context_t * ao_context) +{ + u32 tmp; + wait_queue_head_t queue; + unsigned long flags; + + init_waitqueue_head(&queue); + + CALL_PDEBUG("me4000_ao_stop() is executed\n"); + + /* Set the stop bit */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_STOP; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "me4000_ao_stop():Wait on state machine after stop interrupted\n"); + return -EINTR; + } + } + + /* Clear the stop bit */ + //tmp &= ~ME4000_AO_CTRL_BIT_STOP; + //me4000_outl(tmp, ao_context->ctrl_reg); + + return 0; +} + +static int me4000_ao_immediate_stop(me4000_ao_context_t * ao_context) +{ + u32 tmp; + wait_queue_head_t queue; + unsigned long flags; + + init_waitqueue_head(&queue); + + CALL_PDEBUG("me4000_ao_immediate_stop() is executed\n"); + + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "me4000_ao_immediate_stop():Wait on state machine after stop interrupted\n"); + return -EINTR; + } + } + + /* Clear the stop bits */ + //tmp &= ~(ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); + //me4000_outl(tmp, ao_context->ctrl_reg); + + return 0; +} + +static int me4000_ao_timer_set_divisor(u32 * arg, + me4000_ao_context_t * ao_context) +{ + u32 divisor; + u32 tmp; + + CALL_PDEBUG("me4000_ao_timer set_divisor() is executed\n"); + + if (get_user(divisor, arg)) + return -EFAULT; + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "me4000_ao_timer_set_divisor():Can't set timer while DAC is running\n"); + return -EBUSY; + } + + PDEBUG("me4000_ao_timer set_divisor():Divisor from user = %d\n", + divisor); + + /* Check if the divisor is right. ME4000_AO_MIN_TICKS is the lowest */ + if (divisor < ME4000_AO_MIN_TICKS) { + printk(KERN_ERR + "ME4000:me4000_ao_timer set_divisor():Divisor to low\n"); + return -EINVAL; + } + + /* Fix bug in Firmware */ + divisor -= 2; + + PDEBUG("me4000_ao_timer set_divisor():Divisor to HW = %d\n", divisor); + + /* Write the divisor */ + me4000_outl(divisor, ao_context->timer_reg); + + return 0; +} + +static int me4000_ao_ex_trig_set_edge(int *arg, + me4000_ao_context_t * ao_context) +{ + int mode; + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_ex_trig_set_edge() is executed\n"); + + if (get_user(mode, arg)) + return -EFAULT; + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "me4000_ao_ex_trig_set_edge():Can't set trigger while DAC is running\n"); + return -EBUSY; + } + + if (mode == ME4000_AO_TRIGGER_EXT_EDGE_RISING) { + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp &= + ~(ME4000_AO_CTRL_BIT_EX_TRIG_EDGE | + ME4000_AO_CTRL_BIT_EX_TRIG_BOTH); + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + } else if (mode == ME4000_AO_TRIGGER_EXT_EDGE_FALLING) { + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp &= ~ME4000_AO_CTRL_BIT_EX_TRIG_BOTH; + tmp |= ME4000_AO_CTRL_BIT_EX_TRIG_EDGE; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + } else if (mode == ME4000_AO_TRIGGER_EXT_EDGE_BOTH) { + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= + ME4000_AO_CTRL_BIT_EX_TRIG_EDGE | + ME4000_AO_CTRL_BIT_EX_TRIG_BOTH; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + } else { + printk(KERN_ERR + "me4000_ao_ex_trig_set_edge():Invalid trigger mode\n"); + return -EINVAL; + } + + return 0; +} + +static int me4000_ao_ex_trig_enable(me4000_ao_context_t * ao_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_ex_trig_enable() is executed\n"); + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "me4000_ao_ex_trig_enable():Can't enable trigger while DAC is running\n"); + return -EBUSY; + } + + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + return 0; +} + +static int me4000_ao_ex_trig_disable(me4000_ao_context_t * ao_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_ex_trig_disable() is executed\n"); + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "me4000_ao_ex_trig_disable():Can't disable trigger while DAC is running\n"); + return -EBUSY; + } + + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + return 0; +} + +static int me4000_ao_simultaneous_disable(me4000_ao_context_t * ao_context) +{ + u32 tmp; + + CALL_PDEBUG("me4000_ao_simultaneous_disable() is executed\n"); + + /* Check if the state machine is stopped */ + /* Be careful here because this function is called from + me4000_ao_synchronous disable */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "me4000_ao_simultaneous_disable():Can't disable while DAC is running\n"); + return -EBUSY; + } + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + tmp &= ~(0x1 << ao_context->index); // Disable preload bit + tmp &= ~(0x1 << (ao_context->index + 16)); // Disable hw simultaneous bit + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + + return 0; +} + +static int me4000_ao_simultaneous_ex_trig(me4000_ao_context_t * ao_context) +{ + u32 tmp; + + CALL_PDEBUG("me4000_ao_simultaneous_ex_trig() is executed\n"); + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + tmp |= (0x1 << ao_context->index); // Enable preload bit + tmp |= (0x1 << (ao_context->index + 16)); // Enable hw simultaneous bit + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + + return 0; +} + +static int me4000_ao_simultaneous_sw(me4000_ao_context_t * ao_context) +{ + u32 tmp; + + CALL_PDEBUG("me4000_ao_simultaneous_sw() is executed\n"); + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + tmp |= (0x1 << ao_context->index); // Enable preload bit + tmp &= ~(0x1 << (ao_context->index + 16)); // Disable hw simultaneous bit + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + + return 0; +} + +static int me4000_ao_preload(me4000_ao_context_t * ao_context) +{ + CALL_PDEBUG("me4000_ao_preload() is executed\n"); + return me4000_ao_simultaneous_sw(ao_context); +} + +static int me4000_ao_preload_update(me4000_ao_context_t * ao_context) +{ + u32 tmp; + u32 ctrl; + struct list_head *entry; + + CALL_PDEBUG("me4000_ao_preload_update() is executed\n"); + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + list_for_each(entry, &ao_context->board_info->ao_context_list) { + /* The channels we update must be in the following state : + - Mode A + - Hardware trigger is disabled + - Corresponding simultaneous bit is reset + */ + ctrl = me4000_inl(ao_context->ctrl_reg); + if (! + (ctrl & + (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1 | + ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG))) { + if (! + (tmp & + (0x1 << + (((me4000_ao_context_t *) entry)->index + 16)))) { + tmp &= + ~(0x1 << + (((me4000_ao_context_t *) entry)->index)); + } + } + } + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + + return 0; +} + +static int me4000_ao_simultaneous_update(me4000_ao_channel_list_t * arg, + me4000_ao_context_t * ao_context) +{ + int err; + int i; + u32 tmp; + me4000_ao_channel_list_t channels; + + CALL_PDEBUG("me4000_ao_simultaneous_update() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&channels, arg, sizeof(me4000_ao_channel_list_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ao_simultaneous_update():Can't copy command\n"); + return -EFAULT; + } + + channels.list = + kmalloc(sizeof(unsigned long) * channels.count, GFP_KERNEL); + if (!channels.list) { + printk(KERN_ERR + "ME4000:me4000_ao_simultaneous_update():Can't get buffer\n"); + return -ENOMEM; + } + memset(channels.list, 0, sizeof(unsigned long) * channels.count); + + /* Copy channel list from user */ + err = + copy_from_user(channels.list, arg->list, + sizeof(unsigned long) * channels.count); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ao_simultaneous_update():Can't copy list\n"); + kfree(channels.list); + return -EFAULT; + } + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + for (i = 0; i < channels.count; i++) { + if (channels.list[i] > + ao_context->board_info->board_p->ao.count) { + spin_unlock(&ao_context->board_info->preload_lock); + kfree(channels.list); + printk(KERN_ERR + "ME4000:me4000_ao_simultaneous_update():Invalid board number specified\n"); + return -EFAULT; + } + tmp &= ~(0x1 << channels.list[i]); // Clear the preload bit + tmp &= ~(0x1 << (channels.list[i] + 16)); // Clear the hw simultaneous bit + } + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + kfree(channels.list); + + return 0; +} + +static int me4000_ao_synchronous_ex_trig(me4000_ao_context_t * ao_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_synchronous_ex_trig() is executed\n"); + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR + "me4000_ao_synchronous_ex_trig(): DAC is running\n"); + return -EBUSY; + } + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + tmp &= ~(0x1 << ao_context->index); // Disable synchronous sw bit + tmp |= 0x1 << (ao_context->index + 16); // Enable synchronous hw bit + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + + /* Make runnable */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + if (tmp & (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1)) { + tmp &= + ~(ME4000_AO_CTRL_BIT_STOP | + ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); + me4000_outl(tmp, ao_context->ctrl_reg); + } + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + return 0; +} + +static int me4000_ao_synchronous_sw(me4000_ao_context_t * ao_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_synchronous_sw() is executed\n"); + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR "me4000_ao_synchronous_sw(): DAC is running\n"); + return -EBUSY; + } + + spin_lock(&ao_context->board_info->preload_lock); + tmp = me4000_inl(ao_context->preload_reg); + tmp |= 0x1 << ao_context->index; // Enable synchronous sw bit + tmp &= ~(0x1 << (ao_context->index + 16)); // Disable synchronous hw bit + me4000_outl(tmp, ao_context->preload_reg); + spin_unlock(&ao_context->board_info->preload_lock); + + /* Make runnable */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = me4000_inl(ao_context->ctrl_reg); + if (tmp & (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1)) { + tmp &= + ~(ME4000_AO_CTRL_BIT_STOP | + ME4000_AO_CTRL_BIT_IMMEDIATE_STOP); + me4000_outl(tmp, ao_context->ctrl_reg); + } + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + return 0; +} + +static int me4000_ao_synchronous_disable(me4000_ao_context_t * ao_context) +{ + return me4000_ao_simultaneous_disable(ao_context); +} + +static int me4000_ao_get_free_buffer(unsigned long *arg, + me4000_ao_context_t * ao_context) +{ + unsigned long c; + int err; + + c = me4000_buf_space(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT); + + err = copy_to_user(arg, &c, sizeof(unsigned long)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ao_get_free_buffer():Can't copy to user space\n"); + return -EFAULT; + } + + return 0; +} + +static int me4000_ao_ex_trig_timeout(unsigned long *arg, + me4000_ao_context_t * ao_context) +{ + u32 tmp; + wait_queue_head_t queue; + unsigned long ref; + unsigned long timeout; + + CALL_PDEBUG("me4000_ao_ex_trig_timeout() is executed\n"); + + if (get_user(timeout, arg)) { + printk(KERN_ERR + "me4000_ao_ex_trig_timeout():Cannot copy data from user\n"); + return -EFAULT; + } + + init_waitqueue_head(&queue); + + tmp = inl(ao_context->ctrl_reg); + + if ((tmp & ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG)) { + if (timeout) { + ref = jiffies; + while ((inl(ao_context->status_reg) & + ME4000_AO_STATUS_BIT_FSM)) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ao_ex_trig_timeout():Wait on start of state machine interrupted\n"); + return -EINTR; + } + if (((jiffies - ref) > (timeout * HZ / USER_HZ))) { // 2.6 has diffrent definitions for HZ in user and kernel space + printk(KERN_ERR + "ME4000:me4000_ao_ex_trig_timeout():Timeout reached\n"); + return -EIO; + } + } + } else { + while ((inl(ao_context->status_reg) & + ME4000_AO_STATUS_BIT_FSM)) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ao_ex_trig_timeout():Wait on start of state machine interrupted\n"); + return -EINTR; + } + } + } + } else { + printk(KERN_ERR + "ME4000:me4000_ao_ex_trig_timeout():External Trigger is not enabled\n"); + return -EINVAL; + } + + return 0; +} + +static int me4000_ao_enable_do(me4000_ao_context_t * ao_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_enable_do() is executed\n"); + + /* Only available for analog output 3 */ + if (ao_context->index != 3) { + printk(KERN_ERR + "me4000_ao_enable_do():Only available for analog output 3\n"); + return -ENOTTY; + } + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR "me4000_ao_enable_do(): DAC is running\n"); + return -EBUSY; + } + + /* Set the stop bit */ + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_ENABLE_DO; + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + return 0; +} + +static int me4000_ao_disable_do(me4000_ao_context_t * ao_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ao_disable_do() is executed\n"); + + /* Only available for analog output 3 */ + if (ao_context->index != 3) { + printk(KERN_ERR + "me4000_ao_disable():Only available for analog output 3\n"); + return -ENOTTY; + } + + /* Check if the state machine is stopped */ + tmp = me4000_inl(ao_context->status_reg); + if (tmp & ME4000_AO_STATUS_BIT_FSM) { + printk(KERN_ERR "me4000_ao_disable_do(): DAC is running\n"); + return -EBUSY; + } + + spin_lock_irqsave(&ao_context->int_lock, flags); + tmp = inl(ao_context->ctrl_reg); + tmp &= ~(ME4000_AO_CTRL_BIT_ENABLE_DO); + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock_irqrestore(&ao_context->int_lock, flags); + + return 0; +} + +static int me4000_ao_fsm_state(int *arg, me4000_ao_context_t * ao_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ao_fsm_state() is executed\n"); + + tmp = + (me4000_inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) ? 1 + : 0; + + if (ao_context->pipe_flag) { + printk(KERN_ERR "me4000_ao_fsm_state():Broken pipe detected\n"); + return -EPIPE; + } + + if (put_user(tmp, arg)) { + printk(KERN_ERR "me4000_ao_fsm_state():Cannot copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/*------------------------------- Analog input stuff --------------------------------------*/ + +static int me4000_ai_prepare(me4000_ai_context_t * ai_context) +{ + wait_queue_head_t queue; + int err; + + CALL_PDEBUG("me4000_ai_prepare() is executed\n"); + + init_waitqueue_head(&queue); + + /* Set the new mode and stop bits */ + me4000_outl(ai_context-> + mode | ME4000_AI_CTRL_BIT_STOP | + ME4000_AI_CTRL_BIT_IMMEDIATE_STOP, ai_context->ctrl_reg); + + /* Set the timer registers */ + ai_context->chan_timer = 66; + ai_context->chan_pre_timer = 66; + ai_context->scan_timer_low = 0; + ai_context->scan_timer_high = 0; + + me4000_outl(65, ai_context->chan_timer_reg); + me4000_outl(65, ai_context->chan_pre_timer_reg); + me4000_outl(0, ai_context->scan_timer_low_reg); + me4000_outl(0, ai_context->scan_timer_high_reg); + me4000_outl(0, ai_context->scan_pre_timer_low_reg); + me4000_outl(0, ai_context->scan_pre_timer_high_reg); + + ai_context->channel_list_count = 0; + + if (ai_context->mode) { + /* Request the interrupt line */ + err = + request_irq(ai_context->irq, me4000_ai_isr, + IRQF_DISABLED | IRQF_SHARED, ME4000_NAME, + ai_context); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_prepare():Can't get interrupt line"); + return -ENODEV; + } + + /* Allocate circular buffer */ + ai_context->circ_buf.buf = + kmalloc(ME4000_AI_BUFFER_SIZE, GFP_KERNEL); + if (!ai_context->circ_buf.buf) { + printk(KERN_ERR + "ME4000:me4000_ai_prepare():Can't get circular buffer\n"); + free_irq(ai_context->irq, ai_context); + return -ENOMEM; + } + memset(ai_context->circ_buf.buf, 0, ME4000_AI_BUFFER_SIZE); + + /* Clear the circular buffer */ + ai_context->circ_buf.head = 0; + ai_context->circ_buf.tail = 0; + } + + return 0; +} + +static int me4000_ai_reset(me4000_ai_context_t * ai_context) +{ + wait_queue_head_t queue; + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ai_reset() is executed\n"); + + init_waitqueue_head(&queue); + + /* + * First stop conversion of the state machine before reconfigure. + * If not stopped before configuring mode, it could + * walk in a undefined state. + */ + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + + while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "me4000_ai_reset():Wait on state machine after stop interrupted\n"); + return -EINTR; + } + } + + /* Clear the control register and set the stop bits */ + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + me4000_outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP, + ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + + /* Reset timer registers */ + ai_context->chan_timer = 66; + ai_context->chan_pre_timer = 66; + ai_context->scan_timer_low = 0; + ai_context->scan_timer_high = 0; + ai_context->sample_counter = 0; + ai_context->sample_counter_reload = 0; + + me4000_outl(65, ai_context->chan_timer_reg); + me4000_outl(65, ai_context->chan_pre_timer_reg); + me4000_outl(0, ai_context->scan_timer_low_reg); + me4000_outl(0, ai_context->scan_timer_high_reg); + me4000_outl(0, ai_context->scan_pre_timer_low_reg); + me4000_outl(0, ai_context->scan_pre_timer_high_reg); + me4000_outl(0, ai_context->sample_counter_reg); + + ai_context->channel_list_count = 0; + + /* Clear the circular buffer */ + ai_context->circ_buf.head = 0; + ai_context->circ_buf.tail = 0; + + return 0; +} + +static int me4000_ai_ioctl_sing(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ai_context_t *ai_context; + + CALL_PDEBUG("me4000_ai_ioctl_sing() is executed\n"); + + ai_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + printk(KERN_ERR "me4000_ai_ioctl_sing():Wrong magic number\n"); + return -ENOTTY; + } + if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { + printk(KERN_ERR + "me4000_ai_ioctl_sing():Service number to high\n"); + return -ENOTTY; + } + + switch (service) { + case ME4000_AI_SINGLE: + return me4000_ai_single((me4000_ai_single_t *) arg, ai_context); + case ME4000_AI_EX_TRIG_ENABLE: + return me4000_ai_ex_trig_enable(ai_context); + case ME4000_AI_EX_TRIG_DISABLE: + return me4000_ai_ex_trig_disable(ai_context); + case ME4000_AI_EX_TRIG_SETUP: + return me4000_ai_ex_trig_setup((me4000_ai_trigger_t *) arg, + ai_context); + case ME4000_GET_USER_INFO: + return me4000_get_user_info((me4000_user_info_t *) arg, + ai_context->board_info); + case ME4000_AI_OFFSET_ENABLE: + return me4000_ai_offset_enable(ai_context); + case ME4000_AI_OFFSET_DISABLE: + return me4000_ai_offset_disable(ai_context); + case ME4000_AI_FULLSCALE_ENABLE: + return me4000_ai_fullscale_enable(ai_context); + case ME4000_AI_FULLSCALE_DISABLE: + return me4000_ai_fullscale_disable(ai_context); + case ME4000_AI_EEPROM_READ: + return me4000_eeprom_read((me4000_eeprom_t *) arg, ai_context); + case ME4000_AI_EEPROM_WRITE: + return me4000_eeprom_write((me4000_eeprom_t *) arg, ai_context); + default: + printk(KERN_ERR + "me4000_ai_ioctl_sing():Invalid service number\n"); + return -ENOTTY; + } + return 0; +} + +static int me4000_ai_single(me4000_ai_single_t * arg, + me4000_ai_context_t * ai_context) +{ + me4000_ai_single_t cmd; + int err; + u32 tmp; + wait_queue_head_t queue; + unsigned long jiffy; + + CALL_PDEBUG("me4000_ai_single() is executed\n"); + + init_waitqueue_head(&queue); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_ai_single_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_single():Can't copy from user space\n"); + return -EFAULT; + } + + /* Check range parameter */ + switch (cmd.range) { + case ME4000_AI_LIST_RANGE_BIPOLAR_10: + case ME4000_AI_LIST_RANGE_BIPOLAR_2_5: + case ME4000_AI_LIST_RANGE_UNIPOLAR_10: + case ME4000_AI_LIST_RANGE_UNIPOLAR_2_5: + break; + default: + printk(KERN_ERR + "ME4000:me4000_ai_single():Invalid range specified\n"); + return -EINVAL; + } + + /* Check mode and channel number */ + switch (cmd.mode) { + case ME4000_AI_LIST_INPUT_SINGLE_ENDED: + if (cmd.channel >= ai_context->board_info->board_p->ai.count) { + printk(KERN_ERR + "ME4000:me4000_ai_single():Analog input is not available\n"); + return -EINVAL; + } + break; + case ME4000_AI_LIST_INPUT_DIFFERENTIAL: + if (cmd.channel >= + ai_context->board_info->board_p->ai.diff_count) { + printk(KERN_ERR + "ME4000:me4000_ai_single():Analog input is not available in differential mode\n"); + return -EINVAL; + } + break; + default: + printk(KERN_ERR + "ME4000:me4000_ai_single():Invalid mode specified\n"); + return -EINVAL; + } + + /* Clear channel list, data fifo and both stop bits */ + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= + ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO | + ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); + me4000_outl(tmp, ai_context->ctrl_reg); + + /* Enable channel list and data fifo */ + tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO; + me4000_outl(tmp, ai_context->ctrl_reg); + + /* Generate channel list entry */ + me4000_outl(cmd.channel | cmd.range | cmd. + mode | ME4000_AI_LIST_LAST_ENTRY, + ai_context->channel_list_reg); + + /* Set the timer to maximum */ + me4000_outl(66, ai_context->chan_timer_reg); + me4000_outl(66, ai_context->chan_pre_timer_reg); + + if (tmp & ME4000_AI_CTRL_BIT_EX_TRIG) { + jiffy = jiffies; + while (! + (me4000_inl(ai_context->status_reg) & + ME4000_AI_STATUS_BIT_EF_DATA)) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ai_single():Wait on start of state machine interrupted\n"); + return -EINTR; + } + if (((jiffies - jiffy) > (cmd.timeout * HZ / USER_HZ)) && cmd.timeout) { // 2.6 has diffrent definitions for HZ in user and kernel space + printk(KERN_ERR + "ME4000:me4000_ai_single():Timeout reached\n"); + return -EIO; + } + } + } else { + /* Start conversion */ + me4000_inl(ai_context->start_reg); + + /* Wait until ready */ + udelay(10); + if (! + (me4000_inl(ai_context->status_reg) & + ME4000_AI_STATUS_BIT_EF_DATA)) { + printk(KERN_ERR + "ME4000:me4000_ai_single():Value not available after wait\n"); + return -EIO; + } + } + + /* Read value from data fifo */ + cmd.value = me4000_inl(ai_context->data_reg) & 0xFFFF; + + /* Copy result back to user */ + err = copy_to_user(arg, &cmd, sizeof(me4000_ai_single_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_single():Can't copy to user space\n"); + return -EFAULT; + } + + return 0; +} + +static int me4000_ai_ioctl_sw(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ai_context_t *ai_context; + + CALL_PDEBUG("me4000_ai_ioctl_sw() is executed\n"); + + ai_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + printk(KERN_ERR "me4000_ai_ioctl_sw():Wrong magic number\n"); + return -ENOTTY; + } + if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { + printk(KERN_ERR + "me4000_ai_ioctl_sw():Service number to high\n"); + return -ENOTTY; + } + + switch (service) { + case ME4000_AI_SC_SETUP: + return me4000_ai_sc_setup((me4000_ai_sc_t *) arg, ai_context); + case ME4000_AI_CONFIG: + return me4000_ai_config((me4000_ai_config_t *) arg, ai_context); + case ME4000_AI_START: + return me4000_ai_start(ai_context); + case ME4000_AI_STOP: + return me4000_ai_stop(ai_context); + case ME4000_AI_IMMEDIATE_STOP: + return me4000_ai_immediate_stop(ai_context); + case ME4000_AI_FSM_STATE: + return me4000_ai_fsm_state((int *)arg, ai_context); + case ME4000_GET_USER_INFO: + return me4000_get_user_info((me4000_user_info_t *) arg, + ai_context->board_info); + case ME4000_AI_EEPROM_READ: + return me4000_eeprom_read((me4000_eeprom_t *) arg, ai_context); + case ME4000_AI_EEPROM_WRITE: + return me4000_eeprom_write((me4000_eeprom_t *) arg, ai_context); + case ME4000_AI_GET_COUNT_BUFFER: + return me4000_ai_get_count_buffer((unsigned long *)arg, + ai_context); + default: + printk(KERN_ERR + "ME4000:me4000_ai_ioctl_sw():Invalid service number %d\n", + service); + return -ENOTTY; + } + return 0; +} + +static int me4000_ai_ioctl_ext(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ai_context_t *ai_context; + + CALL_PDEBUG("me4000_ai_ioctl_ext() is executed\n"); + + ai_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + printk(KERN_ERR "me4000_ai_ioctl_ext():Wrong magic number\n"); + return -ENOTTY; + } + if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { + printk(KERN_ERR + "me4000_ai_ioctl_ext():Service number to high\n"); + return -ENOTTY; + } + + switch (service) { + case ME4000_AI_SC_SETUP: + return me4000_ai_sc_setup((me4000_ai_sc_t *) arg, ai_context); + case ME4000_AI_CONFIG: + return me4000_ai_config((me4000_ai_config_t *) arg, ai_context); + case ME4000_AI_START: + return me4000_ai_start_ex((unsigned long *)arg, ai_context); + case ME4000_AI_STOP: + return me4000_ai_stop(ai_context); + case ME4000_AI_IMMEDIATE_STOP: + return me4000_ai_immediate_stop(ai_context); + case ME4000_AI_EX_TRIG_ENABLE: + return me4000_ai_ex_trig_enable(ai_context); + case ME4000_AI_EX_TRIG_DISABLE: + return me4000_ai_ex_trig_disable(ai_context); + case ME4000_AI_EX_TRIG_SETUP: + return me4000_ai_ex_trig_setup((me4000_ai_trigger_t *) arg, + ai_context); + case ME4000_AI_FSM_STATE: + return me4000_ai_fsm_state((int *)arg, ai_context); + case ME4000_GET_USER_INFO: + return me4000_get_user_info((me4000_user_info_t *) arg, + ai_context->board_info); + case ME4000_AI_GET_COUNT_BUFFER: + return me4000_ai_get_count_buffer((unsigned long *)arg, + ai_context); + default: + printk(KERN_ERR + "ME4000:me4000_ai_ioctl_ext():Invalid service number %d\n", + service); + return -ENOTTY; + } + return 0; +} + +static int me4000_ai_fasync(int fd, struct file *file_p, int mode) +{ + me4000_ai_context_t *ai_context; + + CALL_PDEBUG("me4000_ao_fasync_cont() is executed\n"); + + ai_context = file_p->private_data; + return fasync_helper(fd, file_p, mode, &ai_context->fasync_p); +} + +static int me4000_ai_config(me4000_ai_config_t * arg, + me4000_ai_context_t * ai_context) +{ + me4000_ai_config_t cmd; + u32 *list = NULL; + u32 mode; + int i; + int err; + wait_queue_head_t queue; + u64 scan; + u32 tmp; + + CALL_PDEBUG("me4000_ai_config() is executed\n"); + + init_waitqueue_head(&queue); + + /* Check if conversion is stopped */ + if (inl(ai_context->ctrl_reg) & ME4000_AI_STATUS_BIT_FSM) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Conversion is not stopped\n"); + err = -EBUSY; + goto AI_CONFIG_ERR; + } + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_ai_config_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Can't copy from user space\n"); + err = -EFAULT; + goto AI_CONFIG_ERR; + } + + PDEBUG + ("me4000_ai_config():chan = %ld, pre_chan = %ld, scan_low = %ld, scan_high = %ld, count = %ld\n", + cmd.timer.chan, cmd.timer.pre_chan, cmd.timer.scan_low, + cmd.timer.scan_high, cmd.channel_list.count); + + /* Check whether sample and hold is available for this board */ + if (cmd.sh) { + if (!ai_context->board_info->board_p->ai.sh_count) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Sample and Hold is not available for this board\n"); + err = -ENODEV; + goto AI_CONFIG_ERR; + } + } + + /* Check the channel list size */ + if (cmd.channel_list.count > ME4000_AI_CHANNEL_LIST_COUNT) { + printk(KERN_ERR + "me4000_ai_config():Channel list is to large\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + + /* Copy channel list from user */ + list = kmalloc(sizeof(u32) * cmd.channel_list.count, GFP_KERNEL); + if (!list) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Can't get memory for channel list\n"); + err = -ENOMEM; + goto AI_CONFIG_ERR; + } + err = + copy_from_user(list, cmd.channel_list.list, + sizeof(u32) * cmd.channel_list.count); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Can't copy from user space\n"); + err = -EFAULT; + goto AI_CONFIG_ERR; + } + + /* Check if last entry bit is set */ + if (!(list[cmd.channel_list.count - 1] & ME4000_AI_LIST_LAST_ENTRY)) { + printk(KERN_WARNING + "me4000_ai_config():Last entry bit is not set\n"); + list[cmd.channel_list.count - 1] |= ME4000_AI_LIST_LAST_ENTRY; + } + + /* Check whether mode is equal for all entries */ + mode = list[0] & 0x20; + for (i = 0; i < cmd.channel_list.count; i++) { + if ((list[i] & 0x20) != mode) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Mode is not equal for all entries\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + } + + /* Check whether channels are available for this mode */ + if (mode == ME4000_AI_LIST_INPUT_SINGLE_ENDED) { + for (i = 0; i < cmd.channel_list.count; i++) { + if ((list[i] & 0x1F) >= + ai_context->board_info->board_p->ai.count) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Channel is not available for single ended\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + } + } else if (mode == ME4000_AI_LIST_INPUT_DIFFERENTIAL) { + for (i = 0; i < cmd.channel_list.count; i++) { + if ((list[i] & 0x1F) >= + ai_context->board_info->board_p->ai.diff_count) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Channel is not available for differential\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + } + } + + /* Check if bipolar is set for all entries when in differential mode */ + if (mode == ME4000_AI_LIST_INPUT_DIFFERENTIAL) { + for (i = 0; i < cmd.channel_list.count; i++) { + if ((list[i] & 0xC0) != ME4000_AI_LIST_RANGE_BIPOLAR_10 + && (list[i] & 0xC0) != + ME4000_AI_LIST_RANGE_BIPOLAR_2_5) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Bipolar is not selected in differential mode\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + } + } + + if (ai_context->mode != ME4000_AI_ACQ_MODE_EXT_SINGLE_VALUE) { + /* Check for minimum channel divisor */ + if (cmd.timer.chan < ME4000_AI_MIN_TICKS) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Channel timer divisor is to low\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + + /* Check if minimum channel divisor is adjusted when sample and hold is activated */ + if ((cmd.sh) && (cmd.timer.chan != ME4000_AI_MIN_TICKS)) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Channel timer divisor must be at minimum when sample and hold is activated\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + + /* Check for minimum channel pre divisor */ + if (cmd.timer.pre_chan < ME4000_AI_MIN_TICKS) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Channel pre timer divisor is to low\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + + /* Write the channel timers */ + me4000_outl(cmd.timer.chan - 1, ai_context->chan_timer_reg); + me4000_outl(cmd.timer.pre_chan - 1, + ai_context->chan_pre_timer_reg); + + /* Save the timer values in the board context */ + ai_context->chan_timer = cmd.timer.chan; + ai_context->chan_pre_timer = cmd.timer.pre_chan; + + if (ai_context->mode != ME4000_AI_ACQ_MODE_EXT_SINGLE_CHANLIST) { + /* Check for scan timer divisor */ + scan = + (u64) cmd.timer.scan_low | ((u64) cmd.timer. + scan_high << 32); + if (scan != 0) { + if (scan < + cmd.channel_list.count * cmd.timer.chan + + 1) { + printk(KERN_ERR + "ME4000:me4000_ai_config():Scan timer divisor is to low\n"); + err = -EINVAL; + goto AI_CONFIG_ERR; + } + } + + /* Write the scan timers */ + if (scan != 0) { + scan--; + tmp = (u32) (scan & 0xFFFFFFFF); + me4000_outl(tmp, + ai_context->scan_timer_low_reg); + tmp = (u32) ((scan >> 32) & 0xFFFFFFFF); + me4000_outl(tmp, + ai_context->scan_timer_high_reg); + + scan = + scan - (cmd.timer.chan - 1) + + (cmd.timer.pre_chan - 1); + tmp = (u32) (scan & 0xFFFFFFFF); + me4000_outl(tmp, + ai_context->scan_pre_timer_low_reg); + tmp = (u32) ((scan >> 32) & 0xFFFFFFFF); + me4000_outl(tmp, + ai_context-> + scan_pre_timer_high_reg); + } else { + me4000_outl(0x0, + ai_context->scan_timer_low_reg); + me4000_outl(0x0, + ai_context->scan_timer_high_reg); + + me4000_outl(0x0, + ai_context->scan_pre_timer_low_reg); + me4000_outl(0x0, + ai_context-> + scan_pre_timer_high_reg); + } + + ai_context->scan_timer_low = cmd.timer.scan_low; + ai_context->scan_timer_high = cmd.timer.scan_high; + } + } + + /* Clear the channel list */ + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_CHANNEL_FIFO; + me4000_outl(tmp, ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO; + me4000_outl(tmp, ai_context->ctrl_reg); + + /* Write the channel list */ + for (i = 0; i < cmd.channel_list.count; i++) { + me4000_outl(list[i], ai_context->channel_list_reg); + } + + /* Setup sample and hold */ + if (cmd.sh) { + tmp |= ME4000_AI_CTRL_BIT_SAMPLE_HOLD; + me4000_outl(tmp, ai_context->ctrl_reg); + } else { + tmp &= ~ME4000_AI_CTRL_BIT_SAMPLE_HOLD; + me4000_outl(tmp, ai_context->ctrl_reg); + } + + /* Save the channel list size in the board context */ + ai_context->channel_list_count = cmd.channel_list.count; + + kfree(list); + + return 0; + + AI_CONFIG_ERR: + + /* Reset the timers */ + ai_context->chan_timer = 66; + ai_context->chan_pre_timer = 66; + ai_context->scan_timer_low = 0; + ai_context->scan_timer_high = 0; + + me4000_outl(65, ai_context->chan_timer_reg); + me4000_outl(65, ai_context->chan_pre_timer_reg); + me4000_outl(0, ai_context->scan_timer_high_reg); + me4000_outl(0, ai_context->scan_timer_low_reg); + me4000_outl(0, ai_context->scan_pre_timer_high_reg); + me4000_outl(0, ai_context->scan_pre_timer_low_reg); + + ai_context->channel_list_count = 0; + + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= + ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_SAMPLE_HOLD); + + if (list) + kfree(list); + + return err; + +} + +static int ai_common_start(me4000_ai_context_t * ai_context) +{ + u32 tmp; + CALL_PDEBUG("ai_common_start() is executed\n"); + + tmp = me4000_inl(ai_context->ctrl_reg); + + /* Check if conversion is stopped */ + if (tmp & ME4000_AI_STATUS_BIT_FSM) { + printk(KERN_ERR + "ME4000:ai_common_start():Conversion is not stopped\n"); + return -EBUSY; + } + + /* Clear data fifo, disable all interrupts, clear sample counter reload */ + tmp &= ~(ME4000_AI_CTRL_BIT_DATA_FIFO | ME4000_AI_CTRL_BIT_LE_IRQ | + ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_SC_RELOAD); + + me4000_outl(tmp, ai_context->ctrl_reg); + + /* Clear circular buffer */ + ai_context->circ_buf.head = 0; + ai_context->circ_buf.tail = 0; + + /* Enable data fifo */ + tmp |= ME4000_AI_CTRL_BIT_DATA_FIFO; + + /* Determine interrupt setup */ + if (ai_context->sample_counter && !ai_context->sample_counter_reload) { + /* Enable Half Full Interrupt and Sample Counter Interrupt */ + tmp |= ME4000_AI_CTRL_BIT_SC_IRQ | ME4000_AI_CTRL_BIT_HF_IRQ; + } else if (ai_context->sample_counter + && ai_context->sample_counter_reload) { + if (ai_context->sample_counter <= ME4000_AI_FIFO_COUNT / 2) { + /* Enable only Sample Counter Interrupt */ + tmp |= + ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_SC_RELOAD; + } else { + /* Enable Half Full Interrupt and Sample Counter Interrupt */ + tmp |= + ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_HF_IRQ | + ME4000_AI_CTRL_BIT_SC_RELOAD; + } + } else { + /* Enable only Half Full Interrupt */ + tmp |= ME4000_AI_CTRL_BIT_HF_IRQ; + } + + /* Clear the stop bits */ + tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP); + + /* Write setup to hardware */ + me4000_outl(tmp, ai_context->ctrl_reg); + + /* Write sample counter */ + me4000_outl(ai_context->sample_counter, ai_context->sample_counter_reg); + + return 0; +} + +static int me4000_ai_start(me4000_ai_context_t * ai_context) +{ + int err; + CALL_PDEBUG("me4000_ai_start() is executed\n"); + + /* Prepare Hardware */ + err = ai_common_start(ai_context); + if (err) + return err; + + /* Start conversion by dummy read */ + me4000_inl(ai_context->start_reg); + + return 0; +} + +static int me4000_ai_start_ex(unsigned long *arg, + me4000_ai_context_t * ai_context) +{ + int err; + wait_queue_head_t queue; + unsigned long ref; + unsigned long timeout; + + CALL_PDEBUG("me4000_ai_start_ex() is executed\n"); + + if (get_user(timeout, arg)) { + printk(KERN_ERR + "me4000_ai_start_ex():Cannot copy data from user\n"); + return -EFAULT; + } + + init_waitqueue_head(&queue); + + /* Prepare Hardware */ + err = ai_common_start(ai_context); + if (err) + return err; + + if (timeout) { + ref = jiffies; + while (! + (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) + { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ai_start_ex():Wait on start of state machine interrupted\n"); + return -EINTR; + } + if (((jiffies - ref) > (timeout * HZ / USER_HZ))) { // 2.6 has diffrent definitions for HZ in user and kernel space + printk(KERN_ERR + "ME4000:me4000_ai_start_ex():Timeout reached\n"); + return -EIO; + } + } + } else { + while (! + (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) + { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ai_start_ex():Wait on start of state machine interrupted\n"); + return -EINTR; + } + } + } + + return 0; +} + +static int me4000_ai_stop(me4000_ai_context_t * ai_context) +{ + wait_queue_head_t queue; + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ai_stop() is executed\n"); + + init_waitqueue_head(&queue); + + /* Disable irqs and clear data fifo */ + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= + ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_DATA_FIFO); + /* Stop conversion of the state machine */ + tmp |= ME4000_AI_CTRL_BIT_STOP; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + + /* Clear circular buffer */ + ai_context->circ_buf.head = 0; + ai_context->circ_buf.tail = 0; + + while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ai_stop():Wait on state machine after stop interrupted\n"); + return -EINTR; + } + } + + return 0; +} + +static int me4000_ai_immediate_stop(me4000_ai_context_t * ai_context) +{ + wait_queue_head_t queue; + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ai_stop() is executed\n"); + + init_waitqueue_head(&queue); + + /* Disable irqs and clear data fifo */ + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= + ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_DATA_FIFO); + /* Stop conversion of the state machine */ + tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + + /* Clear circular buffer */ + ai_context->circ_buf.head = 0; + ai_context->circ_buf.tail = 0; + + while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) { + interruptible_sleep_on_timeout(&queue, 1); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ai_stop():Wait on state machine after stop interrupted\n"); + return -EINTR; + } + } + + return 0; +} + +static int me4000_ai_ex_trig_enable(me4000_ai_context_t * ai_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ai_ex_trig_enable() is executed\n"); + + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_EX_TRIG; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + + return 0; +} + +static int me4000_ai_ex_trig_disable(me4000_ai_context_t * ai_context) +{ + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ai_ex_trig_disable() is executed\n"); + + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + + return 0; +} + +static int me4000_ai_ex_trig_setup(me4000_ai_trigger_t * arg, + me4000_ai_context_t * ai_context) +{ + me4000_ai_trigger_t cmd; + int err; + u32 tmp; + unsigned long flags; + + CALL_PDEBUG("me4000_ai_ex_trig_setup() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_ai_trigger_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_ex_trig_setup():Can't copy from user space\n"); + return -EFAULT; + } + + spin_lock_irqsave(&ai_context->int_lock, flags); + tmp = me4000_inl(ai_context->ctrl_reg); + + if (cmd.mode == ME4000_AI_TRIGGER_EXT_DIGITAL) { + tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG; + } else if (cmd.mode == ME4000_AI_TRIGGER_EXT_ANALOG) { + if (!ai_context->board_info->board_p->ai.ex_trig_analog) { + printk(KERN_ERR + "ME4000:me4000_ai_ex_trig_setup():No analog trigger available\n"); + return -EINVAL; + } + tmp |= ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG; + } else { + spin_unlock_irqrestore(&ai_context->int_lock, flags); + printk(KERN_ERR + "ME4000:me4000_ai_ex_trig_setup():Invalid trigger mode specified\n"); + return -EINVAL; + } + + if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_RISING) { + tmp &= + ~(ME4000_AI_CTRL_BIT_EX_TRIG_BOTH | + ME4000_AI_CTRL_BIT_EX_TRIG_FALLING); + } else if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_FALLING) { + tmp |= ME4000_AI_CTRL_BIT_EX_TRIG_FALLING; + tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG_BOTH; + } else if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_BOTH) { + tmp |= + ME4000_AI_CTRL_BIT_EX_TRIG_BOTH | + ME4000_AI_CTRL_BIT_EX_TRIG_FALLING; + } else { + spin_unlock_irqrestore(&ai_context->int_lock, flags); + printk(KERN_ERR + "ME4000:me4000_ai_ex_trig_setup():Invalid trigger edge specified\n"); + return -EINVAL; + } + + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock_irqrestore(&ai_context->int_lock, flags); + return 0; +} + +static int me4000_ai_sc_setup(me4000_ai_sc_t * arg, + me4000_ai_context_t * ai_context) +{ + me4000_ai_sc_t cmd; + int err; + + CALL_PDEBUG("me4000_ai_sc_setup() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_ai_sc_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_sc_setup():Can't copy from user space\n"); + return -EFAULT; + } + + ai_context->sample_counter = cmd.value; + ai_context->sample_counter_reload = cmd.reload; + + return 0; +} + +static ssize_t me4000_ai_read(struct file *filep, char *buff, size_t cnt, + loff_t * offp) +{ + me4000_ai_context_t *ai_context = filep->private_data; + s16 *buffer = (s16 *) buff; + size_t count = cnt / 2; + unsigned long flags; + int tmp; + int c = 0; + int k = 0; + int ret = 0; + wait_queue_t wait; + + CALL_PDEBUG("me4000_ai_read() is executed\n"); + + init_waitqueue_entry(&wait, current); + + /* Check count */ + if (count <= 0) { + PDEBUG("me4000_ai_read():Count is 0\n"); + return 0; + } + + while (count > 0) { + if (filep->f_flags & O_NONBLOCK) { + c = me4000_values_to_end(ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT); + if (!c) { + PDEBUG + ("me4000_ai_read():Returning from nonblocking read\n"); + break; + } + } else { + /* Check if conversion is still running */ + if (! + (me4000_inl(ai_context->status_reg) & + ME4000_AI_STATUS_BIT_FSM)) { + printk(KERN_ERR + "ME4000:me4000_ai_read():Conversion interrupted\n"); + return -EPIPE; + } + + wait_event_interruptible(ai_context->wait_queue, + (me4000_values_to_end + (ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT))); + if (signal_pending(current)) { + printk(KERN_ERR + "ME4000:me4000_ai_read():Wait on values interrupted from signal\n"); + return -EINTR; + } + } + + /* Only read count values or as much as available */ + c = me4000_values_to_end(ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT); + PDEBUG("me4000_ai_read():%d values to end\n", c); + if (count < c) + c = count; + + PDEBUG("me4000_ai_read():Copy %d values to user space\n", c); + k = 2 * c; + k -= copy_to_user(buffer, + ai_context->circ_buf.buf + + ai_context->circ_buf.tail, k); + c = k / 2; + if (!c) { + printk(KERN_ERR + "ME4000:me4000_ai_read():Cannot copy new values to user\n"); + return -EFAULT; + } + + ai_context->circ_buf.tail = + (ai_context->circ_buf.tail + c) & (ME4000_AI_BUFFER_COUNT - + 1); + buffer += c; + count -= c; + ret += c; + + spin_lock_irqsave(&ai_context->int_lock, flags); + if (me4000_buf_space + (ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) { + tmp = me4000_inl(ai_context->ctrl_reg); + + /* Determine interrupt setup */ + if (ai_context->sample_counter + && !ai_context->sample_counter_reload) { + /* Enable Half Full Interrupt and Sample Counter Interrupt */ + tmp |= + ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_HF_IRQ; + } else if (ai_context->sample_counter + && ai_context->sample_counter_reload) { + if (ai_context->sample_counter < + ME4000_AI_FIFO_COUNT / 2) { + /* Enable only Sample Counter Interrupt */ + tmp |= ME4000_AI_CTRL_BIT_SC_IRQ; + } else { + /* Enable Half Full Interrupt and Sample Counter Interrupt */ + tmp |= + ME4000_AI_CTRL_BIT_SC_IRQ | + ME4000_AI_CTRL_BIT_HF_IRQ; + } + } else { + /* Enable only Half Full Interrupt */ + tmp |= ME4000_AI_CTRL_BIT_HF_IRQ; + } + + me4000_outl(tmp, ai_context->ctrl_reg); + } + spin_unlock_irqrestore(&ai_context->int_lock, flags); + } + + /* Check if conversion is still running */ + if (!(me4000_inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) { + printk(KERN_ERR + "ME4000:me4000_ai_read():Conversion not running after complete read\n"); + return -EPIPE; + } + + if (filep->f_flags & O_NONBLOCK) { + return (k == 0) ? -EAGAIN : 2 * ret; + } + + CALL_PDEBUG("me4000_ai_read() is leaved\n"); + return ret * 2; +} + +static unsigned int me4000_ai_poll(struct file *file_p, poll_table * wait) +{ + me4000_ai_context_t *ai_context; + unsigned long mask = 0; + + CALL_PDEBUG("me4000_ai_poll() is executed\n"); + + ai_context = file_p->private_data; + + /* Register wait queue */ + poll_wait(file_p, &ai_context->wait_queue, wait); + + /* Get available values */ + if (me4000_values_to_end(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) + mask |= POLLIN | POLLRDNORM; + + PDEBUG("me4000_ai_poll():Return mask %lX\n", mask); + + return mask; +} + +static int me4000_ai_offset_enable(me4000_ai_context_t * ai_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ai_offset_enable() is executed\n"); + + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_OFFSET; + me4000_outl(tmp, ai_context->ctrl_reg); + + return 0; +} + +static int me4000_ai_offset_disable(me4000_ai_context_t * ai_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ai_offset_disable() is executed\n"); + + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_OFFSET; + me4000_outl(tmp, ai_context->ctrl_reg); + + return 0; +} + +static int me4000_ai_fullscale_enable(me4000_ai_context_t * ai_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ai_fullscale_enable() is executed\n"); + + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_FULLSCALE; + me4000_outl(tmp, ai_context->ctrl_reg); + + return 0; +} + +static int me4000_ai_fullscale_disable(me4000_ai_context_t * ai_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ai_fullscale_disable() is executed\n"); + + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_FULLSCALE; + me4000_outl(tmp, ai_context->ctrl_reg); + + return 0; +} + +static int me4000_ai_fsm_state(int *arg, me4000_ai_context_t * ai_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ai_fsm_state() is executed\n"); + + tmp = + (me4000_inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) ? 1 + : 0; + + if (put_user(tmp, arg)) { + printk(KERN_ERR "me4000_ai_fsm_state():Cannot copy to user\n"); + return -EFAULT; + } + + return 0; +} + +static int me4000_ai_get_count_buffer(unsigned long *arg, + me4000_ai_context_t * ai_context) +{ + unsigned long c; + int err; + + c = me4000_buf_count(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT); + + err = copy_to_user(arg, &c, sizeof(unsigned long)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_ai_get_count_buffer():Can't copy to user space\n"); + return -EFAULT; + } + + return 0; +} + +/*---------------------------------- EEPROM stuff ---------------------------*/ + +static int eeprom_write_cmd(me4000_ai_context_t * ai_context, unsigned long cmd, + int length) +{ + int i; + unsigned long value; + + CALL_PDEBUG("eeprom_write_cmd() is executed\n"); + + PDEBUG("eeprom_write_cmd():Write command 0x%08lX with length = %d\n", + cmd, length); + + /* Get the ICR register and clear the related bits */ + value = me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR); + value &= ~(PLX_ICR_MASK_EEPROM); + me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); + + /* Raise the chip select */ + value |= PLX_ICR_BIT_EEPROM_CHIP_SELECT; + me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + for (i = 0; i < length; i++) { + if (cmd & ((0x1 << (length - 1)) >> i)) { + value |= PLX_ICR_BIT_EEPROM_WRITE; + } else { + value &= ~PLX_ICR_BIT_EEPROM_WRITE; + } + + /* Write to EEPROM */ + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + /* Raising edge of the clock */ + value |= PLX_ICR_BIT_EEPROM_CLOCK_SET; + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + /* Falling edge of the clock */ + value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET; + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + } + + /* Clear the chip select */ + value &= ~PLX_ICR_BIT_EEPROM_CHIP_SELECT; + me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + /* Wait until hardware is ready for sure */ + mdelay(10); + + return 0; +} + +static unsigned short eeprom_read_cmd(me4000_ai_context_t * ai_context, + unsigned long cmd, int length) +{ + int i; + unsigned long value; + unsigned short id = 0; + + CALL_PDEBUG("eeprom_read_cmd() is executed\n"); + + PDEBUG("eeprom_read_cmd():Read command 0x%08lX with length = %d\n", cmd, + length); + + /* Get the ICR register and clear the related bits */ + value = me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR); + value &= ~(PLX_ICR_MASK_EEPROM); + + me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); + + /* Raise the chip select */ + value |= PLX_ICR_BIT_EEPROM_CHIP_SELECT; + me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + /* Write the read command to the eeprom */ + for (i = 0; i < length; i++) { + if (cmd & ((0x1 << (length - 1)) >> i)) { + value |= PLX_ICR_BIT_EEPROM_WRITE; + } else { + value &= ~PLX_ICR_BIT_EEPROM_WRITE; + } + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + /* Raising edge of the clock */ + value |= PLX_ICR_BIT_EEPROM_CLOCK_SET; + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + /* Falling edge of the clock */ + value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET; + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + } + + /* Read the value from the eeprom */ + for (i = 0; i < 16; i++) { + /* Raising edge of the clock */ + value |= PLX_ICR_BIT_EEPROM_CLOCK_SET; + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + if (me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR) & + PLX_ICR_BIT_EEPROM_READ) { + id |= (0x8000 >> i); + PDEBUG("eeprom_read_cmd():OR with 0x%04X\n", + (0x8000 >> i)); + } else { + PDEBUG("eeprom_read_cmd():Dont't OR\n"); + } + + /* Falling edge of the clock */ + value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET; + me4000_outl(value, + ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + } + + /* Clear the chip select */ + value &= ~PLX_ICR_BIT_EEPROM_CHIP_SELECT; + me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR); + udelay(EEPROM_DELAY); + + return id; +} + +static int me4000_eeprom_write(me4000_eeprom_t * arg, + me4000_ai_context_t * ai_context) +{ + int err; + me4000_eeprom_t setup; + unsigned long cmd; + unsigned long date_high; + unsigned long date_low; + + CALL_PDEBUG("me4000_eeprom_write() is executed\n"); + + err = copy_from_user(&setup, arg, sizeof(setup)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_eeprom_write():Cannot copy from user\n"); + return err; + } + + /* Enable writing */ + eeprom_write_cmd(ai_context, ME4000_EEPROM_CMD_WRITE_ENABLE, + ME4000_EEPROM_CMD_LENGTH_WRITE_ENABLE); + + /* Command for date */ + date_high = (setup.date & 0xFFFF0000) >> 16; + date_low = (setup.date & 0x0000FFFF); + + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_DATE_HIGH << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + date_high); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_DATE_LOW << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + date_low); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for unipolar 10V offset */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + uni_10_offset); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for unipolar 10V fullscale */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + uni_10_fullscale); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for unipolar 2,5V offset */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + uni_2_5_offset); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for unipolar 2,5V fullscale */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + uni_2_5_fullscale); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for bipolar 10V offset */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + bi_10_offset); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for bipolar 10V fullscale */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + bi_10_fullscale); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for bipolar 2,5V offset */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + bi_2_5_offset); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for bipolar 2,5V fullscale */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + bi_2_5_fullscale); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for differential 10V offset */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + diff_10_offset); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for differential 10V fullscale */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE + << ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + diff_10_fullscale); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for differential 2,5V offset */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET << + ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + diff_2_5_offset); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Command for differential 2,5V fullscale */ + cmd = + ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE + << ME4000_EEPROM_DATA_LENGTH) | (0xFFFF & + (unsigned + long) + setup. + diff_2_5_fullscale); + err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE); + if (err) + return err; + + /* Disable writing */ + eeprom_write_cmd(ai_context, ME4000_EEPROM_CMD_WRITE_DISABLE, + ME4000_EEPROM_CMD_LENGTH_WRITE_DISABLE); + + return 0; +} + +static int me4000_eeprom_read(me4000_eeprom_t * arg, + me4000_ai_context_t * ai_context) +{ + int err; + unsigned long cmd; + me4000_eeprom_t setup; + + CALL_PDEBUG("me4000_eeprom_read() is executed\n"); + + /* Command for date */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_DATE_HIGH; + setup.date = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + setup.date <<= 16; + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_DATE_LOW; + setup.date |= + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for unipolar 10V offset */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET; + setup.uni_10_offset = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for unipolar 10V fullscale */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE; + setup.uni_10_fullscale = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for unipolar 2,5V offset */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET; + setup.uni_2_5_offset = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for unipolar 2,5V fullscale */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE; + setup.uni_2_5_fullscale = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for bipolar 10V offset */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET; + setup.bi_10_offset = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for bipolar 10V fullscale */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE; + setup.bi_10_fullscale = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for bipolar 2,5V offset */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET; + setup.bi_2_5_offset = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for bipolar 2,5V fullscale */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE; + setup.bi_2_5_fullscale = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for differntial 10V offset */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET; + setup.diff_10_offset = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for differential 10V fullscale */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE; + setup.diff_10_fullscale = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for differntial 2,5V offset */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET; + setup.diff_2_5_offset = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + /* Command for differential 2,5V fullscale */ + cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE; + setup.diff_2_5_fullscale = + eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ); + + err = copy_to_user(arg, &setup, sizeof(setup)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_eeprom_read():Cannot copy to user\n"); + return err; + } + + return 0; +} + +/*------------------------------------ DIO stuff ----------------------------------------------*/ + +static int me4000_dio_ioctl(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_dio_context_t *dio_context; + + CALL_PDEBUG("me4000_dio_ioctl() is executed\n"); + + dio_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + printk(KERN_ERR "me4000_dio_ioctl():Wrong magic number\n"); + return -ENOTTY; + } + if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { + printk(KERN_ERR "me4000_dio_ioctl():Service number to high\n"); + return -ENOTTY; + } + + switch (service) { + case ME4000_DIO_CONFIG: + return me4000_dio_config((me4000_dio_config_t *) arg, + dio_context); + case ME4000_DIO_SET_BYTE: + return me4000_dio_set_byte((me4000_dio_byte_t *) arg, + dio_context); + case ME4000_DIO_GET_BYTE: + return me4000_dio_get_byte((me4000_dio_byte_t *) arg, + dio_context); + case ME4000_DIO_RESET: + return me4000_dio_reset(dio_context); + default: + printk(KERN_ERR + "ME4000:me4000_dio_ioctl():Invalid service number %d\n", + service); + return -ENOTTY; + } + return 0; +} + +static int me4000_dio_config(me4000_dio_config_t * arg, + me4000_dio_context_t * dio_context) +{ + me4000_dio_config_t cmd; + u32 tmp; + int err; + + CALL_PDEBUG("me4000_dio_config() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_dio_config_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_dio_config():Can't copy from user space\n"); + return -EFAULT; + } + + /* Check port parameter */ + if (cmd.port >= dio_context->dio_count) { + printk(KERN_ERR + "ME4000:me4000_dio_config():Port %d is not available\n", + cmd.port); + return -EINVAL; + } + + PDEBUG("me4000_dio_config(): port %d, mode %d, function %d\n", cmd.port, + cmd.mode, cmd.function); + + if (cmd.port == ME4000_DIO_PORT_A) { + if (cmd.mode == ME4000_DIO_PORT_INPUT) { + /* Check if opto isolated version */ + if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { + printk(KERN_ERR + "ME4000:me4000_dio_config():Cannot set to input on opto isolated versions\n"); + return -EIO; + } + + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_0 | + ME4000_DIO_CTRL_BIT_MODE_1); + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_0 | + ME4000_DIO_CTRL_BIT_MODE_1); + tmp |= ME4000_DIO_CTRL_BIT_MODE_0; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_0 | + ME4000_DIO_CTRL_BIT_MODE_1 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_0); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_0 | + ME4000_DIO_CTRL_BIT_MODE_1; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_0 | + ME4000_DIO_CTRL_BIT_MODE_1 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_0; + me4000_outl(tmp, dio_context->ctrl_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_dio_config():Mode %d is not available\n", + cmd.mode); + return -EINVAL; + } + } else if (cmd.port == ME4000_DIO_PORT_B) { + if (cmd.mode == ME4000_DIO_PORT_INPUT) { + /* Only do anything when TTL version is installed */ + if ((me4000_inl(dio_context->dir_reg) & 0x1)) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_2 | + ME4000_DIO_CTRL_BIT_MODE_3); + me4000_outl(tmp, dio_context->ctrl_reg); + } + } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { + /* Check if opto isolated version */ + if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { + printk(KERN_ERR + "ME4000:me4000_dio_config():Cannot set to output on opto isolated versions\n"); + return -EIO; + } + + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_2 | + ME4000_DIO_CTRL_BIT_MODE_3); + tmp |= ME4000_DIO_CTRL_BIT_MODE_2; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { + /* Check if opto isolated version */ + if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { + printk(KERN_ERR + "ME4000:me4000_dio_config():Cannot set to FIFO low output on opto isolated versions\n"); + return -EIO; + } + + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_2 | + ME4000_DIO_CTRL_BIT_MODE_3 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_1); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_2 | + ME4000_DIO_CTRL_BIT_MODE_3; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { + /* Check if opto isolated version */ + if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { + printk(KERN_ERR + "ME4000:me4000_dio_config():Cannot set to FIFO high output on opto isolated versions\n"); + return -EIO; + } + + tmp = me4000_inl(dio_context->ctrl_reg); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_2 | + ME4000_DIO_CTRL_BIT_MODE_3 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_1; + me4000_outl(tmp, dio_context->ctrl_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_dio_config():Mode %d is not available\n", + cmd.mode); + return -EINVAL; + } + } else if (cmd.port == ME4000_DIO_PORT_C) { + if (cmd.mode == ME4000_DIO_PORT_INPUT) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_4 | + ME4000_DIO_CTRL_BIT_MODE_5); + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_4 | + ME4000_DIO_CTRL_BIT_MODE_5); + tmp |= ME4000_DIO_CTRL_BIT_MODE_4; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_4 | + ME4000_DIO_CTRL_BIT_MODE_5 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_2); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_4 | + ME4000_DIO_CTRL_BIT_MODE_5; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_4 | + ME4000_DIO_CTRL_BIT_MODE_5 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_2; + me4000_outl(tmp, dio_context->ctrl_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_dio_config():Mode %d is not available\n", + cmd.mode); + return -EINVAL; + } + } else if (cmd.port == ME4000_DIO_PORT_D) { + if (cmd.mode == ME4000_DIO_PORT_INPUT) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_6 | + ME4000_DIO_CTRL_BIT_MODE_7); + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_6 | + ME4000_DIO_CTRL_BIT_MODE_7); + tmp |= ME4000_DIO_CTRL_BIT_MODE_6; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_LOW) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_MODE_6 | + ME4000_DIO_CTRL_BIT_MODE_7 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_3); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_6 | + ME4000_DIO_CTRL_BIT_MODE_7; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.mode == ME4000_DIO_FIFO_HIGH) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp |= + ME4000_DIO_CTRL_BIT_MODE_6 | + ME4000_DIO_CTRL_BIT_MODE_7 | + ME4000_DIO_CTRL_BIT_FIFO_HIGH_3; + me4000_outl(tmp, dio_context->ctrl_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_dio_config():Mode %d is not available\n", + cmd.mode); + return -EINVAL; + } + } else { + printk(KERN_ERR + "ME4000:me4000_dio_config():Port %d is not available\n", + cmd.port); + return -EINVAL; + } + + PDEBUG("me4000_dio_config(): port %d, mode %d, function %d\n", cmd.port, + cmd.mode, cmd.function); + + if ((cmd.mode == ME4000_DIO_FIFO_HIGH) + || (cmd.mode == ME4000_DIO_FIFO_LOW)) { + tmp = me4000_inl(dio_context->ctrl_reg); + tmp &= + ~(ME4000_DIO_CTRL_BIT_FUNCTION_0 | + ME4000_DIO_CTRL_BIT_FUNCTION_1); + if (cmd.function == ME4000_DIO_FUNCTION_PATTERN) { + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.function == ME4000_DIO_FUNCTION_DEMUX) { + tmp |= ME4000_DIO_CTRL_BIT_FUNCTION_0; + me4000_outl(tmp, dio_context->ctrl_reg); + } else if (cmd.function == ME4000_DIO_FUNCTION_MUX) { + tmp |= ME4000_DIO_CTRL_BIT_FUNCTION_1; + me4000_outl(tmp, dio_context->ctrl_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_dio_config():Invalid port function specified\n"); + return -EINVAL; + } + } + + return 0; +} + +static int me4000_dio_set_byte(me4000_dio_byte_t * arg, + me4000_dio_context_t * dio_context) +{ + me4000_dio_byte_t cmd; + int err; + + CALL_PDEBUG("me4000_dio_set_byte() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_dio_byte_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Can't copy from user space\n"); + return -EFAULT; + } + + /* Check port parameter */ + if (cmd.port >= dio_context->dio_count) { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Port %d is not available\n", + cmd.port); + return -EINVAL; + } + + if (cmd.port == ME4000_DIO_PORT_A) { + if ((me4000_inl(dio_context->ctrl_reg) & 0x3) != 0x1) { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", + cmd.port); + return -EIO; + } + me4000_outl(cmd.byte, dio_context->port_0_reg); + } else if (cmd.port == ME4000_DIO_PORT_B) { + if ((me4000_inl(dio_context->ctrl_reg) & 0xC) != 0x4) { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", + cmd.port); + return -EIO; + } + me4000_outl(cmd.byte, dio_context->port_1_reg); + } else if (cmd.port == ME4000_DIO_PORT_C) { + if ((me4000_inl(dio_context->ctrl_reg) & 0x30) != 0x10) { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", + cmd.port); + return -EIO; + } + me4000_outl(cmd.byte, dio_context->port_2_reg); + } else if (cmd.port == ME4000_DIO_PORT_D) { + if ((me4000_inl(dio_context->ctrl_reg) & 0xC0) != 0x40) { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n", + cmd.port); + return -EIO; + } + me4000_outl(cmd.byte, dio_context->port_3_reg); + } else { + printk(KERN_ERR + "ME4000:me4000_dio_set_byte():Port %d is not available\n", + cmd.port); + return -EINVAL; + } + + return 0; +} + +static int me4000_dio_get_byte(me4000_dio_byte_t * arg, + me4000_dio_context_t * dio_context) +{ + me4000_dio_byte_t cmd; + int err; + + CALL_PDEBUG("me4000_dio_get_byte() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_dio_byte_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_dio_get_byte():Can't copy from user space\n"); + return -EFAULT; + } + + /* Check port parameter */ + if (cmd.port >= dio_context->dio_count) { + printk(KERN_ERR + "ME4000:me4000_dio_get_byte():Port %d is not available\n", + cmd.port); + return -EINVAL; + } + + if (cmd.port == ME4000_DIO_PORT_A) { + cmd.byte = me4000_inl(dio_context->port_0_reg) & 0xFF; + } else if (cmd.port == ME4000_DIO_PORT_B) { + cmd.byte = me4000_inl(dio_context->port_1_reg) & 0xFF; + } else if (cmd.port == ME4000_DIO_PORT_C) { + cmd.byte = me4000_inl(dio_context->port_2_reg) & 0xFF; + } else if (cmd.port == ME4000_DIO_PORT_D) { + cmd.byte = me4000_inl(dio_context->port_3_reg) & 0xFF; + } else { + printk(KERN_ERR + "ME4000:me4000_dio_get_byte():Port %d is not available\n", + cmd.port); + return -EINVAL; + } + + /* Copy result back to user */ + err = copy_to_user(arg, &cmd, sizeof(me4000_dio_byte_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_dio_get_byte():Can't copy to user space\n"); + return -EFAULT; + } + + return 0; +} + +static int me4000_dio_reset(me4000_dio_context_t * dio_context) +{ + CALL_PDEBUG("me4000_dio_reset() is executed\n"); + + /* Clear the control register */ + me4000_outl(0, dio_context->ctrl_reg); + + /* Check for opto isolated version */ + if (!(me4000_inl(dio_context->dir_reg) & 0x1)) { + me4000_outl(0x1, dio_context->ctrl_reg); + me4000_outl(0x0, dio_context->port_0_reg); + } + + return 0; +} + +/*------------------------------------ COUNTER STUFF ------------------------------------*/ + +static int me4000_cnt_ioctl(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_cnt_context_t *cnt_context; + + CALL_PDEBUG("me4000_cnt_ioctl() is executed\n"); + + cnt_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + printk(KERN_ERR "me4000_dio_ioctl():Wrong magic number\n"); + return -ENOTTY; + } + if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { + printk(KERN_ERR "me4000_dio_ioctl():Service number to high\n"); + return -ENOTTY; + } + + switch (service) { + case ME4000_CNT_READ: + return me4000_cnt_read((me4000_cnt_t *) arg, cnt_context); + case ME4000_CNT_WRITE: + return me4000_cnt_write((me4000_cnt_t *) arg, cnt_context); + case ME4000_CNT_CONFIG: + return me4000_cnt_config((me4000_cnt_config_t *) arg, + cnt_context); + case ME4000_CNT_RESET: + return me4000_cnt_reset(cnt_context); + default: + printk(KERN_ERR + "ME4000:me4000_dio_ioctl():Invalid service number %d\n", + service); + return -ENOTTY; + } + return 0; +} + +static int me4000_cnt_config(me4000_cnt_config_t * arg, + me4000_cnt_context_t * cnt_context) +{ + me4000_cnt_config_t cmd; + u8 counter; + u8 mode; + int err; + + CALL_PDEBUG("me4000_cnt_config() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_cnt_config_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_cnt_config():Can't copy from user space\n"); + return -EFAULT; + } + + /* Check counter parameter */ + switch (cmd.counter) { + case ME4000_CNT_COUNTER_0: + counter = ME4000_CNT_CTRL_BIT_COUNTER_0; + break; + case ME4000_CNT_COUNTER_1: + counter = ME4000_CNT_CTRL_BIT_COUNTER_1; + break; + case ME4000_CNT_COUNTER_2: + counter = ME4000_CNT_CTRL_BIT_COUNTER_2; + break; + default: + printk(KERN_ERR + "ME4000:me4000_cnt_config():Counter %d is not available\n", + cmd.counter); + return -EINVAL; + } + + /* Check mode parameter */ + switch (cmd.mode) { + case ME4000_CNT_MODE_0: + mode = ME4000_CNT_CTRL_BIT_MODE_0; + break; + case ME4000_CNT_MODE_1: + mode = ME4000_CNT_CTRL_BIT_MODE_1; + break; + case ME4000_CNT_MODE_2: + mode = ME4000_CNT_CTRL_BIT_MODE_2; + break; + case ME4000_CNT_MODE_3: + mode = ME4000_CNT_CTRL_BIT_MODE_3; + break; + case ME4000_CNT_MODE_4: + mode = ME4000_CNT_CTRL_BIT_MODE_4; + break; + case ME4000_CNT_MODE_5: + mode = ME4000_CNT_CTRL_BIT_MODE_5; + break; + default: + printk(KERN_ERR + "ME4000:me4000_cnt_config():Mode %d is not available\n", + cmd.mode); + return -EINVAL; + } + + /* Write the control word */ + me4000_outb((counter | mode | 0x30), cnt_context->ctrl_reg); + + return 0; +} + +static int me4000_cnt_read(me4000_cnt_t * arg, + me4000_cnt_context_t * cnt_context) +{ + me4000_cnt_t cmd; + u8 tmp; + int err; + + CALL_PDEBUG("me4000_cnt_read() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_cnt_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_cnt_read():Can't copy from user space\n"); + return -EFAULT; + } + + /* Read counter */ + switch (cmd.counter) { + case ME4000_CNT_COUNTER_0: + tmp = me4000_inb(cnt_context->counter_0_reg); + cmd.value = tmp; + tmp = me4000_inb(cnt_context->counter_0_reg); + cmd.value |= ((u16) tmp) << 8; + break; + case ME4000_CNT_COUNTER_1: + tmp = me4000_inb(cnt_context->counter_1_reg); + cmd.value = tmp; + tmp = me4000_inb(cnt_context->counter_1_reg); + cmd.value |= ((u16) tmp) << 8; + break; + case ME4000_CNT_COUNTER_2: + tmp = me4000_inb(cnt_context->counter_2_reg); + cmd.value = tmp; + tmp = me4000_inb(cnt_context->counter_2_reg); + cmd.value |= ((u16) tmp) << 8; + break; + default: + printk(KERN_ERR + "ME4000:me4000_cnt_read():Counter %d is not available\n", + cmd.counter); + return -EINVAL; + } + + /* Copy result back to user */ + err = copy_to_user(arg, &cmd, sizeof(me4000_cnt_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_cnt_read():Can't copy to user space\n"); + return -EFAULT; + } + + return 0; +} + +static int me4000_cnt_write(me4000_cnt_t * arg, + me4000_cnt_context_t * cnt_context) +{ + me4000_cnt_t cmd; + u8 tmp; + int err; + + CALL_PDEBUG("me4000_cnt_write() is executed\n"); + + /* Copy data from user */ + err = copy_from_user(&cmd, arg, sizeof(me4000_cnt_t)); + if (err) { + printk(KERN_ERR + "ME4000:me4000_cnt_write():Can't copy from user space\n"); + return -EFAULT; + } + + /* Write counter */ + switch (cmd.counter) { + case ME4000_CNT_COUNTER_0: + tmp = cmd.value & 0xFF; + me4000_outb(tmp, cnt_context->counter_0_reg); + tmp = (cmd.value >> 8) & 0xFF; + me4000_outb(tmp, cnt_context->counter_0_reg); + break; + case ME4000_CNT_COUNTER_1: + tmp = cmd.value & 0xFF; + me4000_outb(tmp, cnt_context->counter_1_reg); + tmp = (cmd.value >> 8) & 0xFF; + me4000_outb(tmp, cnt_context->counter_1_reg); + break; + case ME4000_CNT_COUNTER_2: + tmp = cmd.value & 0xFF; + me4000_outb(tmp, cnt_context->counter_2_reg); + tmp = (cmd.value >> 8) & 0xFF; + me4000_outb(tmp, cnt_context->counter_2_reg); + break; + default: + printk(KERN_ERR + "ME4000:me4000_cnt_write():Counter %d is not available\n", + cmd.counter); + return -EINVAL; + } + + return 0; +} + +static int me4000_cnt_reset(me4000_cnt_context_t * cnt_context) +{ + CALL_PDEBUG("me4000_cnt_reset() is executed\n"); + + /* Set the mode and value for counter 0 */ + me4000_outb(0x30, cnt_context->ctrl_reg); + me4000_outb(0x00, cnt_context->counter_0_reg); + me4000_outb(0x00, cnt_context->counter_0_reg); + + /* Set the mode and value for counter 1 */ + me4000_outb(0x70, cnt_context->ctrl_reg); + me4000_outb(0x00, cnt_context->counter_1_reg); + me4000_outb(0x00, cnt_context->counter_1_reg); + + /* Set the mode and value for counter 2 */ + me4000_outb(0xB0, cnt_context->ctrl_reg); + me4000_outb(0x00, cnt_context->counter_2_reg); + me4000_outb(0x00, cnt_context->counter_2_reg); + + return 0; +} + +/*------------------------------------ External Interrupt stuff ------------------------------------*/ + +static int me4000_ext_int_ioctl(struct inode *inode_p, struct file *file_p, + unsigned int service, unsigned long arg) +{ + me4000_ext_int_context_t *ext_int_context; + + CALL_PDEBUG("me4000_ext_int_ioctl() is executed\n"); + + ext_int_context = file_p->private_data; + + if (_IOC_TYPE(service) != ME4000_MAGIC) { + printk(KERN_ERR "me4000_ext_int_ioctl():Wrong magic number\n"); + return -ENOTTY; + } + if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) { + printk(KERN_ERR + "me4000_ext_int_ioctl():Service number to high\n"); + return -ENOTTY; + } + + switch (service) { + case ME4000_EXT_INT_ENABLE: + return me4000_ext_int_enable(ext_int_context); + case ME4000_EXT_INT_DISABLE: + return me4000_ext_int_disable(ext_int_context); + case ME4000_EXT_INT_COUNT: + return me4000_ext_int_count((unsigned long *)arg, + ext_int_context); + default: + printk(KERN_ERR + "ME4000:me4000_ext_int_ioctl():Invalid service number %d\n", + service); + return -ENOTTY; + } + return 0; +} + +static int me4000_ext_int_enable(me4000_ext_int_context_t * ext_int_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ext_int_enable() is executed\n"); + + tmp = me4000_inl(ext_int_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_EX_IRQ; + me4000_outl(tmp, ext_int_context->ctrl_reg); + + return 0; +} + +static int me4000_ext_int_disable(me4000_ext_int_context_t * ext_int_context) +{ + unsigned long tmp; + + CALL_PDEBUG("me4000_ext_int_disable() is executed\n"); + + tmp = me4000_inl(ext_int_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_EX_IRQ; + me4000_outl(tmp, ext_int_context->ctrl_reg); + + return 0; +} + +static int me4000_ext_int_count(unsigned long *arg, + me4000_ext_int_context_t * ext_int_context) +{ + + CALL_PDEBUG("me4000_ext_int_count() is executed\n"); + + put_user(ext_int_context->int_count, arg); + return 0; +} + +/*------------------------------------ General stuff ------------------------------------*/ + +static int me4000_get_user_info(me4000_user_info_t * arg, + me4000_info_t * board_info) +{ + me4000_user_info_t user_info; + + CALL_PDEBUG("me4000_get_user_info() is executed\n"); + + user_info.board_count = board_info->board_count; + user_info.plx_regbase = board_info->plx_regbase; + user_info.plx_regbase_size = board_info->plx_regbase_size; + user_info.me4000_regbase = board_info->me4000_regbase; + user_info.me4000_regbase_size = board_info->me4000_regbase_size; + user_info.serial_no = board_info->serial_no; + user_info.hw_revision = board_info->hw_revision; + user_info.vendor_id = board_info->vendor_id; + user_info.device_id = board_info->device_id; + user_info.pci_bus_no = board_info->pci_bus_no; + user_info.pci_dev_no = board_info->pci_dev_no; + user_info.pci_func_no = board_info->pci_func_no; + user_info.irq = board_info->irq; + user_info.irq_count = board_info->irq_count; + user_info.driver_version = ME4000_DRIVER_VERSION; + user_info.ao_count = board_info->board_p->ao.count; + user_info.ao_fifo_count = board_info->board_p->ao.fifo_count; + + user_info.ai_count = board_info->board_p->ai.count; + user_info.ai_sh_count = board_info->board_p->ai.sh_count; + user_info.ai_ex_trig_analog = board_info->board_p->ai.ex_trig_analog; + + user_info.dio_count = board_info->board_p->dio.count; + + user_info.cnt_count = board_info->board_p->cnt.count; + + if (copy_to_user(arg, &user_info, sizeof(me4000_user_info_t))) + return -EFAULT; + + return 0; +} + +/*------------------------------------ ISR STUFF ------------------------------------*/ + +static int me4000_ext_int_fasync(int fd, struct file *file_ptr, int mode) +{ + int result = 0; + me4000_ext_int_context_t *ext_int_context; + + CALL_PDEBUG("me4000_ext_int_fasync() is executed\n"); + + ext_int_context = file_ptr->private_data; + + result = + fasync_helper(fd, file_ptr, mode, &ext_int_context->fasync_ptr); + + CALL_PDEBUG("me4000_ext_int_fasync() is leaved\n"); + return result; +} + +static irqreturn_t me4000_ao_isr(int irq, void *dev_id) +{ + u32 tmp; + u32 value; + me4000_ao_context_t *ao_context; + int i; + int c = 0; + int c1 = 0; + //unsigned long before; + //unsigned long after; + + ISR_PDEBUG("me4000_ao_isr() is executed\n"); + + ao_context = dev_id; + + /* Check if irq number is right */ + if (irq != ao_context->irq) { + ISR_PDEBUG("me4000_ao_isr():incorrect interrupt num: %d\n", + irq); + return IRQ_NONE; + } + + /* Check if this DAC rised an interrupt */ + if (! + ((0x1 << (ao_context->index + 3)) & + me4000_inl(ao_context->irq_status_reg))) { + ISR_PDEBUG("me4000_ao_isr():Not this DAC\n"); + return IRQ_NONE; + } + + /* Read status register to find out what happened */ + tmp = me4000_inl(ao_context->status_reg); + + if (!(tmp & ME4000_AO_STATUS_BIT_EF) && (tmp & ME4000_AO_STATUS_BIT_HF) + && (tmp & ME4000_AO_STATUS_BIT_HF)) { + c = ME4000_AO_FIFO_COUNT; + ISR_PDEBUG("me4000_ao_isr():Fifo empty\n"); + } else if ((tmp & ME4000_AO_STATUS_BIT_EF) + && (tmp & ME4000_AO_STATUS_BIT_HF) + && (tmp & ME4000_AO_STATUS_BIT_HF)) { + c = ME4000_AO_FIFO_COUNT / 2; + ISR_PDEBUG("me4000_ao_isr():Fifo under half full\n"); + } else { + c = 0; + ISR_PDEBUG("me4000_ao_isr():Fifo full\n"); + } + + ISR_PDEBUG("me4000_ao_isr():Try to write 0x%04X values\n", c); + + while (1) { + c1 = me4000_values_to_end(ao_context->circ_buf, + ME4000_AO_BUFFER_COUNT); + ISR_PDEBUG("me4000_ao_isr():Values to end = %d\n", c1); + if (c1 > c) + c1 = c; + + if (c1 <= 0) { + ISR_PDEBUG + ("me4000_ao_isr():Work done or buffer empty\n"); + break; + } + //rdtscl(before); + if (((ao_context->fifo_reg & 0xFF) == ME4000_AO_01_FIFO_REG) || + ((ao_context->fifo_reg & 0xFF) == ME4000_AO_03_FIFO_REG)) { + for (i = 0; i < c1; i++) { + value = + ((u32) + (* + (ao_context->circ_buf.buf + + ao_context->circ_buf.tail + i))) << 16; + outl(value, ao_context->fifo_reg); + } + } else + outsw(ao_context->fifo_reg, + ao_context->circ_buf.buf + + ao_context->circ_buf.tail, c1); + + //rdtscl(after); + //printk(KERN_ERR"ME4000:me4000_ao_isr():Time lapse = %lu\n", after - before); + + ao_context->circ_buf.tail = + (ao_context->circ_buf.tail + c1) & (ME4000_AO_BUFFER_COUNT - + 1); + ISR_PDEBUG("me4000_ao_isr():%d values wrote to port 0x%04X\n", + c1, ao_context->fifo_reg); + c -= c1; + } + + /* If there are no values left in the buffer, disable interrupts */ + spin_lock(&ao_context->int_lock); + if (!me4000_buf_count(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) { + ISR_PDEBUG + ("me4000_ao_isr():Disable Interrupt because no values left in buffer\n"); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_IRQ; + me4000_outl(tmp, ao_context->ctrl_reg); + } + spin_unlock(&ao_context->int_lock); + + /* Reset the interrupt */ + spin_lock(&ao_context->int_lock); + tmp = me4000_inl(ao_context->ctrl_reg); + tmp |= ME4000_AO_CTRL_BIT_RESET_IRQ; + me4000_outl(tmp, ao_context->ctrl_reg); + tmp &= ~ME4000_AO_CTRL_BIT_RESET_IRQ; + me4000_outl(tmp, ao_context->ctrl_reg); + + /* If state machine is stopped, flow was interrupted */ + if (!(me4000_inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM)) { + printk(KERN_ERR "ME4000:me4000_ao_isr():Broken pipe\n"); + ao_context->pipe_flag = 1; // Set flag in order to inform write routine + tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_IRQ; // Disable interrupt + } + me4000_outl(tmp, ao_context->ctrl_reg); + spin_unlock(&ao_context->int_lock); + + /* Wake up waiting process */ + wake_up_interruptible(&(ao_context->wait_queue)); + + /* Count the interrupt */ + ao_context->board_info->irq_count++; + + return IRQ_HANDLED; +} + +static irqreturn_t me4000_ai_isr(int irq, void *dev_id) +{ + u32 tmp; + me4000_ai_context_t *ai_context; + int i; + int c = 0; + int c1 = 0; +#ifdef ME4000_ISR_DEBUG + unsigned long before; + unsigned long after; +#endif + + ISR_PDEBUG("me4000_ai_isr() is executed\n"); + +#ifdef ME4000_ISR_DEBUG + rdtscl(before); +#endif + + ai_context = dev_id; + + /* Check if irq number is right */ + if (irq != ai_context->irq) { + ISR_PDEBUG("me4000_ai_isr():incorrect interrupt num: %d\n", + irq); + return IRQ_NONE; + } + + if (me4000_inl(ai_context->irq_status_reg) & + ME4000_IRQ_STATUS_BIT_AI_HF) { + ISR_PDEBUG + ("me4000_ai_isr():Fifo half full interrupt occured\n"); + + /* Read status register to find out what happened */ + tmp = me4000_inl(ai_context->ctrl_reg); + + if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && + !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) + && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { + ISR_PDEBUG("me4000_ai_isr():Fifo full\n"); + c = ME4000_AI_FIFO_COUNT; + + /* FIFO overflow, so stop conversion and disable all interrupts */ + spin_lock(&ai_context->int_lock); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; + tmp &= + ~(ME4000_AI_CTRL_BIT_HF_IRQ | + ME4000_AI_CTRL_BIT_SC_IRQ); + outl(tmp, ai_context->ctrl_reg); + spin_unlock(&ai_context->int_lock); + } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) && + !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) + && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { + ISR_PDEBUG("me4000_ai_isr():Fifo half full\n"); + c = ME4000_AI_FIFO_COUNT / 2; + } else { + c = 0; + ISR_PDEBUG + ("me4000_ai_isr():Can't determine state of fifo\n"); + } + + ISR_PDEBUG("me4000_ai_isr():Try to read %d values\n", c); + + while (1) { + c1 = me4000_space_to_end(ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT); + ISR_PDEBUG("me4000_ai_isr():Space to end = %d\n", c1); + if (c1 > c) + c1 = c; + + if (c1 <= 0) { + ISR_PDEBUG + ("me4000_ai_isr():Work done or buffer full\n"); + break; + } + + insw(ai_context->data_reg, + ai_context->circ_buf.buf + + ai_context->circ_buf.head, c1); + ai_context->circ_buf.head = + (ai_context->circ_buf.head + + c1) & (ME4000_AI_BUFFER_COUNT - 1); + c -= c1; + } + + /* Work is done, so reset the interrupt */ + ISR_PDEBUG + ("me4000_ai_isr():reset interrupt fifo half full interrupt\n"); + spin_lock(&ai_context->int_lock); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET; + me4000_outl(tmp, ai_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock(&ai_context->int_lock); + } + + if (me4000_inl(ai_context->irq_status_reg) & ME4000_IRQ_STATUS_BIT_SC) { + ISR_PDEBUG + ("me4000_ai_isr():Sample counter interrupt occured\n"); + + if (!ai_context->sample_counter_reload) { + ISR_PDEBUG + ("me4000_ai_isr():Single data block available\n"); + + /* Poll data until fifo empty */ + for (i = 0; + (i < ME4000_AI_FIFO_COUNT / 2) + && (inl(ai_context->ctrl_reg) & + ME4000_AI_STATUS_BIT_EF_DATA); i++) { + if (me4000_space_to_end + (ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT)) { + *(ai_context->circ_buf.buf + + ai_context->circ_buf.head) = + inw(ai_context->data_reg); + ai_context->circ_buf.head = + (ai_context->circ_buf.head + + 1) & (ME4000_AI_BUFFER_COUNT - 1); + } else + break; + } + ISR_PDEBUG("me4000_ai_isr():%d values read\n", i); + } else { + if (ai_context->sample_counter <= + ME4000_AI_FIFO_COUNT / 2) { + ISR_PDEBUG + ("me4000_ai_isr():Interrupt from adjustable half full threshold\n"); + + /* Read status register to find out what happened */ + tmp = me4000_inl(ai_context->ctrl_reg); + + if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && + !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) + && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { + ISR_PDEBUG + ("me4000_ai_isr():Fifo full\n"); + c = ME4000_AI_FIFO_COUNT; + + /* FIFO overflow, so stop conversion */ + spin_lock(&ai_context->int_lock); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= + ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; + outl(tmp, ai_context->ctrl_reg); + spin_unlock(&ai_context->int_lock); + } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) + && !(tmp & + ME4000_AI_STATUS_BIT_HF_DATA) + && (tmp & + ME4000_AI_STATUS_BIT_EF_DATA)) { + ISR_PDEBUG + ("me4000_ai_isr():Fifo half full\n"); + c = ME4000_AI_FIFO_COUNT / 2; + } else { + c = ai_context->sample_counter; + ISR_PDEBUG + ("me4000_ai_isr():Sample count values\n"); + } + + ISR_PDEBUG + ("me4000_ai_isr():Try to read %d values\n", + c); + + while (1) { + c1 = me4000_space_to_end(ai_context-> + circ_buf, + ME4000_AI_BUFFER_COUNT); + ISR_PDEBUG + ("me4000_ai_isr():Space to end = %d\n", + c1); + if (c1 > c) + c1 = c; + + if (c1 <= 0) { + ISR_PDEBUG + ("me4000_ai_isr():Work done or buffer full\n"); + break; + } + + insw(ai_context->data_reg, + ai_context->circ_buf.buf + + ai_context->circ_buf.head, c1); + ai_context->circ_buf.head = + (ai_context->circ_buf.head + + c1) & (ME4000_AI_BUFFER_COUNT - 1); + c -= c1; + } + } else { + ISR_PDEBUG + ("me4000_ai_isr():Multiple data block available\n"); + + /* Read status register to find out what happened */ + tmp = me4000_inl(ai_context->ctrl_reg); + + if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) && + !(tmp & ME4000_AI_STATUS_BIT_HF_DATA) + && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) { + ISR_PDEBUG + ("me4000_ai_isr():Fifo full\n"); + c = ME4000_AI_FIFO_COUNT; + + /* FIFO overflow, so stop conversion */ + spin_lock(&ai_context->int_lock); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= + ME4000_AI_CTRL_BIT_IMMEDIATE_STOP; + outl(tmp, ai_context->ctrl_reg); + spin_unlock(&ai_context->int_lock); + + while (1) { + c1 = me4000_space_to_end + (ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT); + ISR_PDEBUG + ("me4000_ai_isr():Space to end = %d\n", + c1); + if (c1 > c) + c1 = c; + + if (c1 <= 0) { + ISR_PDEBUG + ("me4000_ai_isr():Work done or buffer full\n"); + break; + } + + insw(ai_context->data_reg, + ai_context->circ_buf.buf + + ai_context->circ_buf.head, + c1); + ai_context->circ_buf.head = + (ai_context->circ_buf.head + + c1) & + (ME4000_AI_BUFFER_COUNT - + 1); + c -= c1; + } + } else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) + && !(tmp & + ME4000_AI_STATUS_BIT_HF_DATA) + && (tmp & + ME4000_AI_STATUS_BIT_EF_DATA)) { + ISR_PDEBUG + ("me4000_ai_isr():Fifo half full\n"); + c = ME4000_AI_FIFO_COUNT / 2; + + while (1) { + c1 = me4000_space_to_end + (ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT); + ISR_PDEBUG + ("me4000_ai_isr():Space to end = %d\n", + c1); + if (c1 > c) + c1 = c; + + if (c1 <= 0) { + ISR_PDEBUG + ("me4000_ai_isr():Work done or buffer full\n"); + break; + } + + insw(ai_context->data_reg, + ai_context->circ_buf.buf + + ai_context->circ_buf.head, + c1); + ai_context->circ_buf.head = + (ai_context->circ_buf.head + + c1) & + (ME4000_AI_BUFFER_COUNT - + 1); + c -= c1; + } + } else { + /* Poll data until fifo empty */ + for (i = 0; + (i < ME4000_AI_FIFO_COUNT / 2) + && (inl(ai_context->ctrl_reg) & + ME4000_AI_STATUS_BIT_EF_DATA); + i++) { + if (me4000_space_to_end + (ai_context->circ_buf, + ME4000_AI_BUFFER_COUNT)) { + *(ai_context->circ_buf. + buf + + ai_context->circ_buf. + head) = + inw(ai_context->data_reg); + ai_context->circ_buf. + head = + (ai_context-> + circ_buf.head + + 1) & + (ME4000_AI_BUFFER_COUNT + - 1); + } else + break; + } + ISR_PDEBUG + ("me4000_ai_isr():%d values read\n", + i); + } + } + } + + /* Work is done, so reset the interrupt */ + ISR_PDEBUG + ("me4000_ai_isr():reset interrupt from sample counter\n"); + spin_lock(&ai_context->int_lock); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET; + me4000_outl(tmp, ai_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET; + me4000_outl(tmp, ai_context->ctrl_reg); + spin_unlock(&ai_context->int_lock); + } + + /* Values are now available, so wake up waiting process */ + if (me4000_buf_count(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) { + ISR_PDEBUG("me4000_ai_isr():Wake up waiting process\n"); + wake_up_interruptible(&(ai_context->wait_queue)); + } + + /* If there is no space left in the buffer, disable interrupts */ + spin_lock(&ai_context->int_lock); + if (!me4000_buf_space(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) { + ISR_PDEBUG + ("me4000_ai_isr():Disable Interrupt because no space left in buffer\n"); + tmp = me4000_inl(ai_context->ctrl_reg); + tmp &= + ~(ME4000_AI_CTRL_BIT_SC_IRQ | ME4000_AI_CTRL_BIT_HF_IRQ | + ME4000_AI_CTRL_BIT_LE_IRQ); + me4000_outl(tmp, ai_context->ctrl_reg); + } + spin_unlock(&ai_context->int_lock); + +#ifdef ME4000_ISR_DEBUG + rdtscl(after); + printk(KERN_ERR "ME4000:me4000_ai_isr():Time lapse = %lu\n", + after - before); +#endif + + return IRQ_HANDLED; +} + +static irqreturn_t me4000_ext_int_isr(int irq, void *dev_id) +{ + me4000_ext_int_context_t *ext_int_context; + unsigned long tmp; + + ISR_PDEBUG("me4000_ext_int_isr() is executed\n"); + + ext_int_context = dev_id; + + /* Check if irq number is right */ + if (irq != ext_int_context->irq) { + ISR_PDEBUG("me4000_ext_int_isr():incorrect interrupt num: %d\n", + irq); + return IRQ_NONE; + } + + if (me4000_inl(ext_int_context->irq_status_reg) & + ME4000_IRQ_STATUS_BIT_EX) { + ISR_PDEBUG("me4000_ext_int_isr():External interrupt occured\n"); + tmp = me4000_inl(ext_int_context->ctrl_reg); + tmp |= ME4000_AI_CTRL_BIT_EX_IRQ_RESET; + me4000_outl(tmp, ext_int_context->ctrl_reg); + tmp &= ~ME4000_AI_CTRL_BIT_EX_IRQ_RESET; + me4000_outl(tmp, ext_int_context->ctrl_reg); + + ext_int_context->int_count++; + + if (ext_int_context->fasync_ptr) { + ISR_PDEBUG + ("me2600_ext_int_isr():Send signal to process\n"); + kill_fasync(&ext_int_context->fasync_ptr, SIGIO, + POLL_IN); + } + } + + return IRQ_HANDLED; +} + +void __exit me4000_module_exit(void) +{ + struct list_head *board_p; + me4000_info_t *board_info; + + CALL_PDEBUG("cleanup_module() is executed\n"); + + unregister_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME); + + unregister_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME); + + unregister_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME); + + unregister_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME); + + unregister_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME); + + remove_proc_entry("me4000", NULL); + + pci_unregister_driver(&me4000_driver); + + /* Reset the boards */ + for (board_p = me4000_board_info_list.next; + board_p != &me4000_board_info_list; board_p = board_p->next) { + board_info = list_entry(board_p, me4000_info_t, list); + me4000_reset_board(board_info); + } + + clear_board_info_list(); +} + +module_exit(me4000_module_exit); + +static int me4000_read_procmem(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + int len = 0; + int limit = count - 1000; + me4000_info_t *board_info; + struct list_head *ptr; + + len += sprintf(buf + len, "\nME4000 DRIVER VERSION %X.%X.%X\n\n", + (ME4000_DRIVER_VERSION & 0xFF0000) >> 16, + (ME4000_DRIVER_VERSION & 0xFF00) >> 8, + (ME4000_DRIVER_VERSION & 0xFF)); + + /* Search for the board context */ + for (ptr = me4000_board_info_list.next; + (ptr != &me4000_board_info_list) && (len < limit); + ptr = ptr->next) { + board_info = list_entry(ptr, me4000_info_t, list); + + len += + sprintf(buf + len, "Board number %d:\n", + board_info->board_count); + len += sprintf(buf + len, "---------------\n"); + len += + sprintf(buf + len, "PLX base register = 0x%lX\n", + board_info->plx_regbase); + len += + sprintf(buf + len, "PLX base register size = 0x%lX\n", + board_info->plx_regbase_size); + len += + sprintf(buf + len, "ME4000 base register = 0x%lX\n", + board_info->me4000_regbase); + len += + sprintf(buf + len, "ME4000 base register size = 0x%lX\n", + board_info->me4000_regbase_size); + len += + sprintf(buf + len, "Serial number = 0x%X\n", + board_info->serial_no); + len += + sprintf(buf + len, "Hardware revision = 0x%X\n", + board_info->hw_revision); + len += + sprintf(buf + len, "Vendor id = 0x%X\n", + board_info->vendor_id); + len += + sprintf(buf + len, "Device id = 0x%X\n", + board_info->device_id); + len += + sprintf(buf + len, "PCI bus number = %d\n", + board_info->pci_bus_no); + len += + sprintf(buf + len, "PCI device number = %d\n", + board_info->pci_dev_no); + len += + sprintf(buf + len, "PCI function number = %d\n", + board_info->pci_func_no); + len += sprintf(buf + len, "IRQ = %u\n", board_info->irq); + len += + sprintf(buf + len, + "Count of interrupts since module was loaded = %d\n", + board_info->irq_count); + + len += + sprintf(buf + len, "Count of analog outputs = %d\n", + board_info->board_p->ao.count); + len += + sprintf(buf + len, "Count of analog output fifos = %d\n", + board_info->board_p->ao.fifo_count); + + len += + sprintf(buf + len, "Count of analog inputs = %d\n", + board_info->board_p->ai.count); + len += + sprintf(buf + len, + "Count of sample and hold devices for analog input = %d\n", + board_info->board_p->ai.sh_count); + len += + sprintf(buf + len, + "Analog external trigger available for analog input = %d\n", + board_info->board_p->ai.ex_trig_analog); + + len += + sprintf(buf + len, "Count of digital ports = %d\n", + board_info->board_p->dio.count); + + len += + sprintf(buf + len, "Count of counter devices = %d\n", + board_info->board_p->cnt.count); + len += + sprintf(buf + len, "AI control register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AI_CTRL_REG)); + + len += sprintf(buf + len, "AO 0 control register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_00_CTRL_REG)); + len += + sprintf(buf + len, "AO 0 status register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_00_STATUS_REG)); + len += + sprintf(buf + len, "AO 1 control register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_01_CTRL_REG)); + len += + sprintf(buf + len, "AO 1 status register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_01_STATUS_REG)); + len += + sprintf(buf + len, "AO 2 control register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_02_CTRL_REG)); + len += + sprintf(buf + len, "AO 2 status register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_02_STATUS_REG)); + len += + sprintf(buf + len, "AO 3 control register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_03_CTRL_REG)); + len += + sprintf(buf + len, "AO 3 status register = 0x%08X\n", + inl(board_info->me4000_regbase + + ME4000_AO_03_STATUS_REG)); + } + + *eof = 1; + return len; +} diff --git a/drivers/staging/me4000/me4000.h b/drivers/staging/me4000/me4000.h new file mode 100644 index 000000000000..c35e4b9793a0 --- /dev/null +++ b/drivers/staging/me4000/me4000.h @@ -0,0 +1,954 @@ +/* + * Copyright (C) 2003 Meilhaus Electronic GmbH (support@meilhaus.de) + * + * Source File : me4000.h + * Author : GG (Guenter Gebhardt) + */ + +#ifndef _ME4000_H_ +#define _ME4000_H_ + +#ifdef __KERNEL__ + +/*============================================================================= + The version of the driver release + ===========================================================================*/ + +#define ME4000_DRIVER_VERSION 0x10009 // Version 1.00.09 + +/*============================================================================= + Debug section + ===========================================================================*/ + +#undef ME4000_CALL_DEBUG // Debug function entry and exit +#undef ME4000_ISR_DEBUG // Debug the interrupt service routine +#undef ME4000_PORT_DEBUG // Debug port access +#undef ME4000_DEBUG // General purpose debug masseges + +#ifdef ME4000_CALL_DEBUG +#undef CALL_PDEBUG +#define CALL_PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args) +#else +# define CALL_PDEBUG(fmt, args...) // no debugging, do nothing +#endif + +#ifdef ME4000_ISR_DEBUG +#undef ISR_PDEBUG +#define ISR_PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args) +#else +#define ISR_PDEBUG(fmt, args...) // no debugging, do nothing +#endif + +#ifdef ME4000_PORT_DEBUG +#undef PORT_PDEBUG +#define PORT_PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args) +#else +#define PORT_PDEBUG(fmt, args...) // no debugging, do nothing +#endif + +#ifdef ME4000_DEBUG +#undef PDEBUG +#define PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args) +#else +#define PDEBUG(fmt, args...) // no debugging, do nothing +#endif + +/*============================================================================= + PCI vendor and device IDs + ===========================================================================*/ + +#define PCI_VENDOR_ID_MEILHAUS 0x1402 + +#define PCI_DEVICE_ID_MEILHAUS_ME4650 0x4650 // Low Cost version + +#define PCI_DEVICE_ID_MEILHAUS_ME4660 0x4660 // Standard version +#define PCI_DEVICE_ID_MEILHAUS_ME4660I 0x4661 // Isolated version +#define PCI_DEVICE_ID_MEILHAUS_ME4660S 0x4662 // Standard version with Sample and Hold +#define PCI_DEVICE_ID_MEILHAUS_ME4660IS 0x4663 // Isolated version with Sample and Hold + +#define PCI_DEVICE_ID_MEILHAUS_ME4670 0x4670 // Standard version +#define PCI_DEVICE_ID_MEILHAUS_ME4670I 0x4671 // Isolated version +#define PCI_DEVICE_ID_MEILHAUS_ME4670S 0x4672 // Standard version with Sample and Hold +#define PCI_DEVICE_ID_MEILHAUS_ME4670IS 0x4673 // Isolated version with Sample and Hold + +#define PCI_DEVICE_ID_MEILHAUS_ME4680 0x4680 // Standard version +#define PCI_DEVICE_ID_MEILHAUS_ME4680I 0x4681 // Isolated version +#define PCI_DEVICE_ID_MEILHAUS_ME4680S 0x4682 // Standard version with Sample and Hold +#define PCI_DEVICE_ID_MEILHAUS_ME4680IS 0x4683 // Isolated version with Sample and Hold + +/*============================================================================= + Device names, for entries in /proc/.. + ===========================================================================*/ + +#define ME4000_NAME "me4000" +#define ME4000_AO_NAME "me4000_ao" +#define ME4000_AI_NAME "me4000_ai" +#define ME4000_DIO_NAME "me4000_dio" +#define ME4000_CNT_NAME "me4000_cnt" +#define ME4000_EXT_INT_NAME "me4000_ext_int" + +/*============================================================================= + ME-4000 base register offsets + ===========================================================================*/ + +#define ME4000_AO_00_CTRL_REG 0x00 // R/W +#define ME4000_AO_00_STATUS_REG 0x04 // R/_ +#define ME4000_AO_00_FIFO_REG 0x08 // _/W +#define ME4000_AO_00_SINGLE_REG 0x0C // R/W +#define ME4000_AO_00_TIMER_REG 0x10 // _/W + +#define ME4000_AO_01_CTRL_REG 0x18 // R/W +#define ME4000_AO_01_STATUS_REG 0x1C // R/_ +#define ME4000_AO_01_FIFO_REG 0x20 // _/W +#define ME4000_AO_01_SINGLE_REG 0x24 // R/W +#define ME4000_AO_01_TIMER_REG 0x28 // _/W + +#define ME4000_AO_02_CTRL_REG 0x30 // R/W +#define ME4000_AO_02_STATUS_REG 0x34 // R/_ +#define ME4000_AO_02_FIFO_REG 0x38 // _/W +#define ME4000_AO_02_SINGLE_REG 0x3C // R/W +#define ME4000_AO_02_TIMER_REG 0x40 // _/W + +#define ME4000_AO_03_CTRL_REG 0x48 // R/W +#define ME4000_AO_03_STATUS_REG 0x4C // R/_ +#define ME4000_AO_03_FIFO_REG 0x50 // _/W +#define ME4000_AO_03_SINGLE_REG 0x54 // R/W +#define ME4000_AO_03_TIMER_REG 0x58 // _/W + +#define ME4000_AI_CTRL_REG 0x74 // _/W +#define ME4000_AI_STATUS_REG 0x74 // R/_ +#define ME4000_AI_CHANNEL_LIST_REG 0x78 // _/W +#define ME4000_AI_DATA_REG 0x7C // R/_ +#define ME4000_AI_CHAN_TIMER_REG 0x80 // _/W +#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84 // _/W +#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88 // _/W +#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8C // _/W +#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90 // _/W +#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 // _/W +#define ME4000_AI_START_REG 0x98 // R/_ + +#define ME4000_IRQ_STATUS_REG 0x9C // R/_ + +#define ME4000_DIO_PORT_0_REG 0xA0 // R/W +#define ME4000_DIO_PORT_1_REG 0xA4 // R/W +#define ME4000_DIO_PORT_2_REG 0xA8 // R/W +#define ME4000_DIO_PORT_3_REG 0xAC // R/W +#define ME4000_DIO_DIR_REG 0xB0 // R/W + +#define ME4000_AO_LOADSETREG_XX 0xB4 // R/W + +#define ME4000_DIO_CTRL_REG 0xB8 // R/W + +#define ME4000_AO_DEMUX_ADJUST_REG 0xBC // -/W + +#define ME4000_AI_SAMPLE_COUNTER_REG 0xC0 // _/W + +/*============================================================================= + Value to adjust Demux + ===========================================================================*/ + +#define ME4000_AO_DEMUX_ADJUST_VALUE 0x4C + +/*============================================================================= + Counter base register offsets + ===========================================================================*/ + +#define ME4000_CNT_COUNTER_0_REG 0x00 +#define ME4000_CNT_COUNTER_1_REG 0x01 +#define ME4000_CNT_COUNTER_2_REG 0x02 +#define ME4000_CNT_CTRL_REG 0x03 + +/*============================================================================= + PLX base register offsets + ===========================================================================*/ + +#define PLX_INTCSR 0x4C // Interrupt control and status register +#define PLX_ICR 0x50 // Initialization control register + +/*============================================================================= + Bits for the PLX_ICSR register + ===========================================================================*/ + +#define PLX_INTCSR_LOCAL_INT1_EN 0x01 // If set, local interrupt 1 is enabled (r/w) +#define PLX_INTCSR_LOCAL_INT1_POL 0x02 // If set, local interrupt 1 polarity is active high (r/w) +#define PLX_INTCSR_LOCAL_INT1_STATE 0x04 // If set, local interrupt 1 is active (r/_) +#define PLX_INTCSR_LOCAL_INT2_EN 0x08 // If set, local interrupt 2 is enabled (r/w) +#define PLX_INTCSR_LOCAL_INT2_POL 0x10 // If set, local interrupt 2 polarity is active high (r/w) +#define PLX_INTCSR_LOCAL_INT2_STATE 0x20 // If set, local interrupt 2 is active (r/_) +#define PLX_INTCSR_PCI_INT_EN 0x40 // If set, PCI interrupt is enabled (r/w) +#define PLX_INTCSR_SOFT_INT 0x80 // If set, a software interrupt is generated (r/w) + +/*============================================================================= + Bits for the PLX_ICR register + ===========================================================================*/ + +#define PLX_ICR_BIT_EEPROM_CLOCK_SET 0x01000000 +#define PLX_ICR_BIT_EEPROM_CHIP_SELECT 0x02000000 +#define PLX_ICR_BIT_EEPROM_WRITE 0x04000000 +#define PLX_ICR_BIT_EEPROM_READ 0x08000000 +#define PLX_ICR_BIT_EEPROM_VALID 0x10000000 + +#define PLX_ICR_MASK_EEPROM 0x1F000000 + +#define EEPROM_DELAY 1 + +/*============================================================================= + Bits for the ME4000_AO_CTRL_REG register + ===========================================================================*/ + +#define ME4000_AO_CTRL_BIT_MODE_0 0x001 +#define ME4000_AO_CTRL_BIT_MODE_1 0x002 +#define ME4000_AO_CTRL_MASK_MODE 0x003 +#define ME4000_AO_CTRL_BIT_STOP 0x004 +#define ME4000_AO_CTRL_BIT_ENABLE_FIFO 0x008 +#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG 0x010 +#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE 0x020 +#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP 0x080 +#define ME4000_AO_CTRL_BIT_ENABLE_DO 0x100 +#define ME4000_AO_CTRL_BIT_ENABLE_IRQ 0x200 +#define ME4000_AO_CTRL_BIT_RESET_IRQ 0x400 +#define ME4000_AO_CTRL_BIT_EX_TRIG_BOTH 0x800 + +/*============================================================================= + Bits for the ME4000_AO_STATUS_REG register + ===========================================================================*/ + +#define ME4000_AO_STATUS_BIT_FSM 0x01 +#define ME4000_AO_STATUS_BIT_FF 0x02 +#define ME4000_AO_STATUS_BIT_HF 0x04 +#define ME4000_AO_STATUS_BIT_EF 0x08 + +/*============================================================================= + Bits for the ME4000_AI_CTRL_REG register + ===========================================================================*/ + +#define ME4000_AI_CTRL_BIT_MODE_0 0x00000001 +#define ME4000_AI_CTRL_BIT_MODE_1 0x00000002 +#define ME4000_AI_CTRL_BIT_MODE_2 0x00000004 +#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD 0x00000008 +#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP 0x00000010 +#define ME4000_AI_CTRL_BIT_STOP 0x00000020 +#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO 0x00000040 +#define ME4000_AI_CTRL_BIT_DATA_FIFO 0x00000080 +#define ME4000_AI_CTRL_BIT_FULLSCALE 0x00000100 +#define ME4000_AI_CTRL_BIT_OFFSET 0x00000200 +#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG 0x00000400 +#define ME4000_AI_CTRL_BIT_EX_TRIG 0x00000800 +#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING 0x00001000 +#define ME4000_AI_CTRL_BIT_EX_IRQ 0x00002000 +#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET 0x00004000 +#define ME4000_AI_CTRL_BIT_LE_IRQ 0x00008000 +#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET 0x00010000 +#define ME4000_AI_CTRL_BIT_HF_IRQ 0x00020000 +#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET 0x00040000 +#define ME4000_AI_CTRL_BIT_SC_IRQ 0x00080000 +#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET 0x00100000 +#define ME4000_AI_CTRL_BIT_SC_RELOAD 0x00200000 +#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH 0x80000000 + +/*============================================================================= + Bits for the ME4000_AI_STATUS_REG register + ===========================================================================*/ + +#define ME4000_AI_STATUS_BIT_EF_CHANNEL 0x00400000 +#define ME4000_AI_STATUS_BIT_HF_CHANNEL 0x00800000 +#define ME4000_AI_STATUS_BIT_FF_CHANNEL 0x01000000 +#define ME4000_AI_STATUS_BIT_EF_DATA 0x02000000 +#define ME4000_AI_STATUS_BIT_HF_DATA 0x04000000 +#define ME4000_AI_STATUS_BIT_FF_DATA 0x08000000 +#define ME4000_AI_STATUS_BIT_LE 0x10000000 +#define ME4000_AI_STATUS_BIT_FSM 0x20000000 + +/*============================================================================= + Bits for the ME4000_IRQ_STATUS_REG register + ===========================================================================*/ + +#define ME4000_IRQ_STATUS_BIT_EX 0x01 +#define ME4000_IRQ_STATUS_BIT_LE 0x02 +#define ME4000_IRQ_STATUS_BIT_AI_HF 0x04 +#define ME4000_IRQ_STATUS_BIT_AO_0_HF 0x08 +#define ME4000_IRQ_STATUS_BIT_AO_1_HF 0x10 +#define ME4000_IRQ_STATUS_BIT_AO_2_HF 0x20 +#define ME4000_IRQ_STATUS_BIT_AO_3_HF 0x40 +#define ME4000_IRQ_STATUS_BIT_SC 0x80 + +/*============================================================================= + Bits for the ME4000_DIO_CTRL_REG register + ===========================================================================*/ + +#define ME4000_DIO_CTRL_BIT_MODE_0 0X0001 +#define ME4000_DIO_CTRL_BIT_MODE_1 0X0002 +#define ME4000_DIO_CTRL_BIT_MODE_2 0X0004 +#define ME4000_DIO_CTRL_BIT_MODE_3 0X0008 +#define ME4000_DIO_CTRL_BIT_MODE_4 0X0010 +#define ME4000_DIO_CTRL_BIT_MODE_5 0X0020 +#define ME4000_DIO_CTRL_BIT_MODE_6 0X0040 +#define ME4000_DIO_CTRL_BIT_MODE_7 0X0080 + +#define ME4000_DIO_CTRL_BIT_FUNCTION_0 0X0100 +#define ME4000_DIO_CTRL_BIT_FUNCTION_1 0X0200 + +#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0 0X0400 +#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1 0X0800 +#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2 0X1000 +#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3 0X2000 + +/*============================================================================= + Bits for the ME4000_CNT_CTRL_REG register + ===========================================================================*/ + +#define ME4000_CNT_CTRL_BIT_COUNTER_0 0x00 +#define ME4000_CNT_CTRL_BIT_COUNTER_1 0x40 +#define ME4000_CNT_CTRL_BIT_COUNTER_2 0x80 + +#define ME4000_CNT_CTRL_BIT_MODE_0 0x00 // Change state if zero crossing +#define ME4000_CNT_CTRL_BIT_MODE_1 0x02 // Retriggerable One-Shot +#define ME4000_CNT_CTRL_BIT_MODE_2 0x04 // Asymmetrical divider +#define ME4000_CNT_CTRL_BIT_MODE_3 0x06 // Symmetrical divider +#define ME4000_CNT_CTRL_BIT_MODE_4 0x08 // Counter start by software trigger +#define ME4000_CNT_CTRL_BIT_MODE_5 0x0A // Counter start by hardware trigger + +/*============================================================================= + Extract information from minor device number + ===========================================================================*/ + +#define AO_BOARD(dev) ((MINOR(dev) >> 6) & 0x3) +#define AO_PORT(dev) ((MINOR(dev) >> 2) & 0xF) +#define AO_MODE(dev) (MINOR(dev) & 0x3) + +#define AI_BOARD(dev) ((MINOR(dev) >> 3) & 0x1F) +#define AI_MODE(dev) (MINOR(dev) & 0x7) + +#define DIO_BOARD(dev) (MINOR(dev)) + +#define CNT_BOARD(dev) (MINOR(dev)) + +#define EXT_INT_BOARD(dev) (MINOR(dev)) + +/*============================================================================= + Circular buffer used for analog input/output reads/writes. + ===========================================================================*/ + +typedef struct me4000_circ_buf { + s16 *buf; + int volatile head; + int volatile tail; +} me4000_circ_buf_t; + +/*============================================================================= + Information about the hardware capabilities + ===========================================================================*/ + +typedef struct me4000_ao_info { + int count; + int fifo_count; +} me4000_ao_info_t; + +typedef struct me4000_ai_info { + int count; + int sh_count; + int diff_count; + int ex_trig_analog; +} me4000_ai_info_t; + +typedef struct me4000_dio_info { + int count; +} me4000_dio_info_t; + +typedef struct me4000_cnt_info { + int count; +} me4000_cnt_info_t; + +typedef struct me4000_board { + u16 vendor_id; + u16 device_id; + me4000_ao_info_t ao; + me4000_ai_info_t ai; + me4000_dio_info_t dio; + me4000_cnt_info_t cnt; +} me4000_board_t; + +static me4000_board_t me4000_boards[] = { + {PCI_VENDOR_ID_MEILHAUS, 0x4610, {0, 0}, {16, 0, 0, 0}, {4}, {3}}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4650, {0, 0}, {16, 0, 0, 0}, {4}, {0}}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4660, {2, 0}, {16, 0, 0, 0}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4661, {2, 0}, {16, 0, 0, 0}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4662, {2, 0}, {16, 8, 0, 0}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4663, {2, 0}, {16, 8, 0, 0}, {4}, {3}}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4670, {4, 0}, {32, 0, 16, 1}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4671, {4, 0}, {32, 0, 16, 1}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4672, {4, 0}, {32, 8, 16, 1}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4673, {4, 0}, {32, 8, 16, 1}, {4}, {3}}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4680, {4, 4}, {32, 0, 16, 1}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4681, {4, 4}, {32, 0, 16, 1}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4682, {4, 4}, {32, 8, 16, 1}, {4}, {3}}, + {PCI_VENDOR_ID_MEILHAUS, 0x4683, {4, 4}, {32, 8, 16, 1}, {4}, {3}}, + + {0}, +}; + +#define ME4000_BOARD_VERSIONS (sizeof(me4000_boards) / sizeof(me4000_board_t) - 1) + +/*============================================================================= + PCI device table. + This is used by modprobe to translate PCI IDs to drivers. + ===========================================================================*/ + +static struct pci_device_id me4000_pci_table[] __devinitdata = { + {PCI_VENDOR_ID_MEILHAUS, 0x4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4661, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4662, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + + {PCI_VENDOR_ID_MEILHAUS, 0x4680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_MEILHAUS, 0x4683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + + {0} +}; + +MODULE_DEVICE_TABLE(pci, me4000_pci_table); + +/*============================================================================= + Global board and subdevice information structures + ===========================================================================*/ + +typedef struct me4000_info { + struct list_head list; // List of all detected boards + int board_count; // Index of the board after detection + + unsigned long plx_regbase; // PLX configuration space base address + unsigned long me4000_regbase; // Base address of the ME4000 + unsigned long timer_regbase; // Base address of the timer circuit + unsigned long program_regbase; // Base address to set the program pin for the xilinx + + unsigned long plx_regbase_size; // PLX register set space + unsigned long me4000_regbase_size; // ME4000 register set space + unsigned long timer_regbase_size; // Timer circuit register set space + unsigned long program_regbase_size; // Size of program base address of the ME4000 + + unsigned int serial_no; // Serial number of the board + unsigned char hw_revision; // Hardware revision of the board + unsigned short vendor_id; // Meilhaus vendor id (0x1402) + unsigned short device_id; // Device ID + + int pci_bus_no; // PCI bus number + int pci_dev_no; // PCI device number + int pci_func_no; // PCI function number + struct pci_dev *pci_dev_p; // General PCI information + + me4000_board_t *board_p; // Holds the board capabilities + + unsigned int irq; // IRQ assigned from the PCI BIOS + unsigned int irq_count; // Count of external interrupts + + spinlock_t preload_lock; // Guards the analog output preload register + spinlock_t ai_ctrl_lock; // Guards the analog input control register + + struct list_head ao_context_list; // List with analog output specific context + struct me4000_ai_context *ai_context; // Analog input specific context + struct me4000_dio_context *dio_context; // Digital I/O specific context + struct me4000_cnt_context *cnt_context; // Counter specific context + struct me4000_ext_int_context *ext_int_context; // External interrupt specific context +} me4000_info_t; + +typedef struct me4000_ao_context { + struct list_head list; // linked list of me4000_ao_context_t + int index; // Index in the list + int mode; // Indicates mode (0 = single, 1 = wraparound, 2 = continous) + int dac_in_use; // Indicates if already opend + spinlock_t use_lock; // Guards in_use + spinlock_t int_lock; // Used when locking out interrupts + me4000_circ_buf_t circ_buf; // Circular buffer + wait_queue_head_t wait_queue; // Wait queue to sleep while blocking write + me4000_info_t *board_info; + unsigned int irq; // The irq associated with this ADC + int volatile pipe_flag; // Indicates broken pipe set from me4000_ao_isr() + 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; + struct fasync_struct *fasync_p; // Queue for asynchronous notification +} me4000_ao_context_t; + +typedef struct me4000_ai_context { + struct list_head list; // linked list of me4000_ai_info_t + int mode; // Indicates mode + int in_use; // Indicates if already opend + spinlock_t use_lock; // Guards in_use + spinlock_t int_lock; // Used when locking out interrupts + int number; // Number of the DAC + unsigned int irq; // The irq associated with this ADC + me4000_circ_buf_t circ_buf; // Circular buffer + wait_queue_head_t wait_queue; // Wait queue to sleep while blocking read + me4000_info_t *board_info; + + struct fasync_struct *fasync_p; // Queue for asynchronous notification + + 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 long chan_timer; + unsigned long chan_pre_timer; + unsigned long scan_timer_low; + unsigned long scan_timer_high; + unsigned long channel_list_count; + unsigned long sample_counter; + int sample_counter_reload; +} me4000_ai_context_t; + +typedef struct me4000_dio_context { + struct list_head list; // linked list of me4000_dio_context_t + int in_use; // Indicates if already opend + spinlock_t use_lock; // Guards in_use + int number; + int dio_count; + me4000_info_t *board_info; + unsigned long dir_reg; + unsigned long ctrl_reg; + unsigned long port_0_reg; + unsigned long port_1_reg; + unsigned long port_2_reg; + unsigned long port_3_reg; +} me4000_dio_context_t; + +typedef struct me4000_cnt_context { + struct list_head list; // linked list of me4000_dio_context_t + int in_use; // Indicates if already opend + spinlock_t use_lock; // Guards in_use + int number; + int cnt_count; + me4000_info_t *board_info; + unsigned long ctrl_reg; + unsigned long counter_0_reg; + unsigned long counter_1_reg; + unsigned long counter_2_reg; +} me4000_cnt_context_t; + +typedef struct me4000_ext_int_context { + struct list_head list; // linked list of me4000_dio_context_t + int in_use; // Indicates if already opend + spinlock_t use_lock; // Guards in_use + int number; + me4000_info_t *board_info; + unsigned int irq; + unsigned long int_count; + struct fasync_struct *fasync_ptr; + unsigned long ctrl_reg; + unsigned long irq_status_reg; +} me4000_ext_int_context_t; + +#endif + +/*============================================================================= + Application include section starts here + ===========================================================================*/ + +/*----------------------------------------------------------------------------- + Defines for analog input + ----------------------------------------------------------------------------*/ + +/* General stuff */ +#define ME4000_AI_FIFO_COUNT 2048 + +#define ME4000_AI_MIN_TICKS 66 +#define ME4000_AI_MAX_SCAN_TICKS 0xFFFFFFFFFFLL + +#define ME4000_AI_BUFFER_SIZE (32 * 1024) // Size in bytes + +#define ME4000_AI_BUFFER_COUNT ((ME4000_AI_BUFFER_SIZE) / 2) // Size in values + +/* Channel list defines and masks */ +#define ME4000_AI_CHANNEL_LIST_COUNT 1024 + +#define ME4000_AI_LIST_INPUT_SINGLE_ENDED 0x000 +#define ME4000_AI_LIST_INPUT_DIFFERENTIAL 0x020 + +#define ME4000_AI_LIST_RANGE_BIPOLAR_10 0x000 +#define ME4000_AI_LIST_RANGE_BIPOLAR_2_5 0x040 +#define ME4000_AI_LIST_RANGE_UNIPOLAR_10 0x080 +#define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5 0x0C0 + +#define ME4000_AI_LIST_LAST_ENTRY 0x100 + +/* External trigger defines */ +#define ME4000_AI_TRIGGER_SOFTWARE 0x0 // Use only with API +#define ME4000_AI_TRIGGER_EXT_DIGITAL 0x1 +#define ME4000_AI_TRIGGER_EXT_ANALOG 0x2 + +#define ME4000_AI_TRIGGER_EXT_EDGE_RISING 0x0 +#define ME4000_AI_TRIGGER_EXT_EDGE_FALLING 0x1 +#define ME4000_AI_TRIGGER_EXT_EDGE_BOTH 0x2 + +/* Sample and Hold */ +#define ME4000_AI_SIMULTANEOUS_DISABLE 0x0 +#define ME4000_AI_SIMULTANEOUS_ENABLE 0x1 + +/* Defines for the Sample Counter */ +#define ME4000_AI_SC_RELOAD 0x0 +#define ME4000_AI_SC_ONCE 0x1 + +/* Modes for analog input */ +#define ME4000_AI_ACQ_MODE_SINGLE 0x00 // Catch one single value +#define ME4000_AI_ACQ_MODE_SOFTWARE 0x01 // Continous sampling with software start +#define ME4000_AI_ACQ_MODE_EXT 0x02 // Continous sampling with external trigger start +#define ME4000_AI_ACQ_MODE_EXT_SINGLE_VALUE 0x03 // Sample one value by external trigger +#define ME4000_AI_ACQ_MODE_EXT_SINGLE_CHANLIST 0x04 // Sample one channel list by external trigger + +/* Staus of AI FSM */ +#define ME4000_AI_STATUS_IDLE 0x0 +#define ME4000_AI_STATUS_BUSY 0x1 + +/* Voltages for calibration */ +#define ME4000_AI_GAIN_1_UNI_OFFSET 10.0E-3 +#define ME4000_AI_GAIN_1_UNI_FULLSCALE 9950.0E-3 +#define ME4000_AI_GAIN_1_BI_OFFSET 0.0 +#define ME4000_AI_GAIN_1_BI_FULLSCALE 9950.0E-3 +#define ME4000_AI_GAIN_4_UNI_OFFSET 10.0E-3 +#define ME4000_AI_GAIN_4_UNI_FULLSCALE 2450.0E-3 +#define ME4000_AI_GAIN_4_BI_OFFSET 0.0 +#define ME4000_AI_GAIN_4_BI_FULLSCALE 2450.0E-3 + +/* Ideal digits for calibration */ +#define ME4000_AI_GAIN_1_UNI_OFFSET_DIGITS (-32702) +#define ME4000_AI_GAIN_1_UNI_FULLSCALE_DIGITS 32440 +#define ME4000_AI_GAIN_1_BI_OFFSET_DIGITS 0 +#define ME4000_AI_GAIN_1_BI_FULLSCALE_DIGITS 32604 +#define ME4000_AI_GAIN_4_UNI_OFFSET_DIGITS (-32505) +#define ME4000_AI_GAIN_4_UNI_FULLSCALE_DIGITS 31457 +#define ME4000_AI_GAIN_4_BI_OFFSET_DIGITS 0 +#define ME4000_AI_GAIN_4_BI_FULLSCALE_DIGITS 32113 + +/*----------------------------------------------------------------------------- + Defines for analog output + ----------------------------------------------------------------------------*/ + +/* General stuff */ +#define ME4000_AO_FIFO_COUNT (4 * 1024) + +#define ME4000_AO_MIN_TICKS 66 + +#define ME4000_AO_BUFFER_SIZE (32 * 1024) // Size in bytes + +#define ME4000_AO_BUFFER_COUNT ((ME4000_AO_BUFFER_SIZE) / 2) // Size in values + +/* Conversion modes for analog output */ +#define ME4000_AO_CONV_MODE_SINGLE 0x0 +#define ME4000_AO_CONV_MODE_WRAPAROUND 0x1 +#define ME4000_AO_CONV_MODE_CONTINUOUS 0x2 + +/* Trigger setup */ +#define ME4000_AO_TRIGGER_EXT_EDGE_RISING 0x0 +#define ME4000_AO_TRIGGER_EXT_EDGE_FALLING 0x1 +#define ME4000_AO_TRIGGER_EXT_EDGE_BOTH 0x2 + +/* Status of AO FSM */ +#define ME4000_AO_STATUS_IDLE 0x0 +#define ME4000_AO_STATUS_BUSY 0x1 + +/*----------------------------------------------------------------------------- + Defines for eeprom + ----------------------------------------------------------------------------*/ + +#define ME4000_EEPROM_CMD_READ 0x180 +#define ME4000_EEPROM_CMD_WRITE_ENABLE 0x130 +#define ME4000_EEPROM_CMD_WRITE_DISABLE 0x100 +#define ME4000_EEPROM_CMD_WRITE 0x1400000 + +#define ME4000_EEPROM_CMD_LENGTH_READ 9 +#define ME4000_EEPROM_CMD_LENGTH_WRITE_ENABLE 9 +#define ME4000_EEPROM_CMD_LENGTH_WRITE_DISABLE 9 +#define ME4000_EEPROM_CMD_LENGTH_WRITE 25 + +#define ME4000_EEPROM_ADR_DATE_HIGH 0x32 +#define ME4000_EEPROM_ADR_DATE_LOW 0x33 + +#define ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET 0x34 +#define ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE 0x35 +#define ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET 0x36 +#define ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE 0x37 +#define ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET 0x38 +#define ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE 0x39 + +#define ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET 0x3A +#define ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE 0x3B +#define ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET 0x3C +#define ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE 0x3D +#define ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET 0x3E +#define ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE 0x3F + +#define ME4000_EEPROM_ADR_LENGTH 6 +#define ME4000_EEPROM_DATA_LENGTH 16 + +/*----------------------------------------------------------------------------- + Defines for digital I/O + ----------------------------------------------------------------------------*/ + +#define ME4000_DIO_PORT_A 0x0 +#define ME4000_DIO_PORT_B 0x1 +#define ME4000_DIO_PORT_C 0x2 +#define ME4000_DIO_PORT_D 0x3 + +#define ME4000_DIO_PORT_INPUT 0x0 +#define ME4000_DIO_PORT_OUTPUT 0x1 +#define ME4000_DIO_FIFO_LOW 0x2 +#define ME4000_DIO_FIFO_HIGH 0x3 + +#define ME4000_DIO_FUNCTION_PATTERN 0x0 +#define ME4000_DIO_FUNCTION_DEMUX 0x1 +#define ME4000_DIO_FUNCTION_MUX 0x2 + +/*----------------------------------------------------------------------------- + Defines for counters + ----------------------------------------------------------------------------*/ + +#define ME4000_CNT_COUNTER_0 0 +#define ME4000_CNT_COUNTER_1 1 +#define ME4000_CNT_COUNTER_2 2 + +#define ME4000_CNT_MODE_0 0 // Change state if zero crossing +#define ME4000_CNT_MODE_1 1 // Retriggerable One-Shot +#define ME4000_CNT_MODE_2 2 // Asymmetrical divider +#define ME4000_CNT_MODE_3 3 // Symmetrical divider +#define ME4000_CNT_MODE_4 4 // Counter start by software trigger +#define ME4000_CNT_MODE_5 5 // Counter start by hardware trigger + +/*----------------------------------------------------------------------------- + General type definitions + ----------------------------------------------------------------------------*/ + +typedef struct me4000_user_info { + int board_count; // Index of the board after detection + unsigned long plx_regbase; // PLX configuration space base address + unsigned long me4000_regbase; // Base address of the ME4000 + unsigned long plx_regbase_size; // PLX register set space + unsigned long me4000_regbase_size; // ME4000 register set space + unsigned long serial_no; // Serial number of the board + unsigned char hw_revision; // Hardware revision of the board + unsigned short vendor_id; // Meilhaus vendor id (0x1402) + unsigned short device_id; // Device ID + int pci_bus_no; // PCI bus number + int pci_dev_no; // PCI device number + int pci_func_no; // PCI function number + char irq; // IRQ assigned from the PCI BIOS + int irq_count; // Count of external interrupts + + int driver_version; // Version of the driver release + + int ao_count; // Count of analog output channels + int ao_fifo_count; // Count fo analog output fifos + + int ai_count; // Count of analog input channels + int ai_sh_count; // Count of sample and hold devices + int ai_ex_trig_analog; // Flag to indicate if analogous external trigger is available + + int dio_count; // Count of digital I/O ports + + int cnt_count; // Count of counters +} me4000_user_info_t; + +/*----------------------------------------------------------------------------- + Type definitions for analog output + ----------------------------------------------------------------------------*/ + +typedef struct me4000_ao_channel_list { + unsigned long count; + unsigned long *list; +} me4000_ao_channel_list_t; + +/*----------------------------------------------------------------------------- + Type definitions for analog input + ----------------------------------------------------------------------------*/ + +typedef struct me4000_ai_channel_list { + unsigned long count; + unsigned long *list; +} me4000_ai_channel_list_t; + +typedef struct me4000_ai_timer { + unsigned long pre_chan; + unsigned long chan; + unsigned long scan_low; + unsigned long scan_high; +} me4000_ai_timer_t; + +typedef struct me4000_ai_config { + me4000_ai_timer_t timer; + me4000_ai_channel_list_t channel_list; + int sh; +} me4000_ai_config_t; + +typedef struct me4000_ai_single { + int channel; + int range; + int mode; + short value; + unsigned long timeout; +} me4000_ai_single_t; + +typedef struct me4000_ai_trigger { + int mode; + int edge; +} me4000_ai_trigger_t; + +typedef struct me4000_ai_sc { + unsigned long value; + int reload; +} me4000_ai_sc_t; + +/*----------------------------------------------------------------------------- + Type definitions for eeprom + ----------------------------------------------------------------------------*/ + +typedef struct me4000_eeprom { + unsigned long date; + short uni_10_offset; + short uni_10_fullscale; + short uni_2_5_offset; + short uni_2_5_fullscale; + short bi_10_offset; + short bi_10_fullscale; + short bi_2_5_offset; + short bi_2_5_fullscale; + short diff_10_offset; + short diff_10_fullscale; + short diff_2_5_offset; + short diff_2_5_fullscale; +} me4000_eeprom_t; + +/*----------------------------------------------------------------------------- + Type definitions for digital I/O + ----------------------------------------------------------------------------*/ + +typedef struct me4000_dio_config { + int port; + int mode; + int function; +} me4000_dio_config_t; + +typedef struct me4000_dio_byte { + int port; + unsigned char byte; +} me4000_dio_byte_t; + +/*----------------------------------------------------------------------------- + Type definitions for counters + ----------------------------------------------------------------------------*/ + +typedef struct me4000_cnt { + int counter; + unsigned short value; +} me4000_cnt_t; + +typedef struct me4000_cnt_config { + int counter; + int mode; +} me4000_cnt_config_t; + +/*----------------------------------------------------------------------------- + Type definitions for external interrupt + ----------------------------------------------------------------------------*/ + +typedef struct { + int int1_count; + int int2_count; +} me4000_int_type; + +/*----------------------------------------------------------------------------- + The ioctls of the board + ----------------------------------------------------------------------------*/ + +#define ME4000_IOCTL_MAXNR 50 +#define ME4000_MAGIC 'y' +#define ME4000_GET_USER_INFO _IOR (ME4000_MAGIC, 0, me4000_user_info_t) + +#define ME4000_AO_START _IOW (ME4000_MAGIC, 1, unsigned long) +#define ME4000_AO_STOP _IO (ME4000_MAGIC, 2) +#define ME4000_AO_IMMEDIATE_STOP _IO (ME4000_MAGIC, 3) +#define ME4000_AO_RESET _IO (ME4000_MAGIC, 4) +#define ME4000_AO_PRELOAD _IO (ME4000_MAGIC, 5) +#define ME4000_AO_PRELOAD_UPDATE _IO (ME4000_MAGIC, 6) +#define ME4000_AO_EX_TRIG_ENABLE _IO (ME4000_MAGIC, 7) +#define ME4000_AO_EX_TRIG_DISABLE _IO (ME4000_MAGIC, 8) +#define ME4000_AO_EX_TRIG_SETUP _IOW (ME4000_MAGIC, 9, int) +#define ME4000_AO_TIMER_SET_DIVISOR _IOW (ME4000_MAGIC, 10, unsigned long) +#define ME4000_AO_ENABLE_DO _IO (ME4000_MAGIC, 11) +#define ME4000_AO_DISABLE_DO _IO (ME4000_MAGIC, 12) +#define ME4000_AO_FSM_STATE _IOR (ME4000_MAGIC, 13, int) + +#define ME4000_AI_SINGLE _IOR (ME4000_MAGIC, 14, me4000_ai_single_t) +#define ME4000_AI_START _IOW (ME4000_MAGIC, 15, unsigned long) +#define ME4000_AI_STOP _IO (ME4000_MAGIC, 16) +#define ME4000_AI_IMMEDIATE_STOP _IO (ME4000_MAGIC, 17) +#define ME4000_AI_EX_TRIG_ENABLE _IO (ME4000_MAGIC, 18) +#define ME4000_AI_EX_TRIG_DISABLE _IO (ME4000_MAGIC, 19) +#define ME4000_AI_EX_TRIG_SETUP _IOW (ME4000_MAGIC, 20, me4000_ai_trigger_t) +#define ME4000_AI_CONFIG _IOW (ME4000_MAGIC, 21, me4000_ai_config_t) +#define ME4000_AI_SC_SETUP _IOW (ME4000_MAGIC, 22, me4000_ai_sc_t) +#define ME4000_AI_FSM_STATE _IOR (ME4000_MAGIC, 23, int) + +#define ME4000_DIO_CONFIG _IOW (ME4000_MAGIC, 24, me4000_dio_config_t) +#define ME4000_DIO_GET_BYTE _IOR (ME4000_MAGIC, 25, me4000_dio_byte_t) +#define ME4000_DIO_SET_BYTE _IOW (ME4000_MAGIC, 26, me4000_dio_byte_t) +#define ME4000_DIO_RESET _IO (ME4000_MAGIC, 27) + +#define ME4000_CNT_READ _IOR (ME4000_MAGIC, 28, me4000_cnt_t) +#define ME4000_CNT_WRITE _IOW (ME4000_MAGIC, 29, me4000_cnt_t) +#define ME4000_CNT_CONFIG _IOW (ME4000_MAGIC, 30, me4000_cnt_config_t) +#define ME4000_CNT_RESET _IO (ME4000_MAGIC, 31) + +#define ME4000_EXT_INT_DISABLE _IO (ME4000_MAGIC, 32) +#define ME4000_EXT_INT_ENABLE _IO (ME4000_MAGIC, 33) +#define ME4000_EXT_INT_COUNT _IOR (ME4000_MAGIC, 34, int) + +#define ME4000_AI_OFFSET_ENABLE _IO (ME4000_MAGIC, 35) +#define ME4000_AI_OFFSET_DISABLE _IO (ME4000_MAGIC, 36) +#define ME4000_AI_FULLSCALE_ENABLE _IO (ME4000_MAGIC, 37) +#define ME4000_AI_FULLSCALE_DISABLE _IO (ME4000_MAGIC, 38) + +#define ME4000_AI_EEPROM_READ _IOR (ME4000_MAGIC, 39, me4000_eeprom_t) +#define ME4000_AI_EEPROM_WRITE _IOW (ME4000_MAGIC, 40, me4000_eeprom_t) + +#define ME4000_AO_SIMULTANEOUS_EX_TRIG _IO (ME4000_MAGIC, 41) +#define ME4000_AO_SIMULTANEOUS_SW _IO (ME4000_MAGIC, 42) +#define ME4000_AO_SIMULTANEOUS_DISABLE _IO (ME4000_MAGIC, 43) +#define ME4000_AO_SIMULTANEOUS_UPDATE _IOW (ME4000_MAGIC, 44, me4000_ao_channel_list_t) + +#define ME4000_AO_SYNCHRONOUS_EX_TRIG _IO (ME4000_MAGIC, 45) +#define ME4000_AO_SYNCHRONOUS_SW _IO (ME4000_MAGIC, 46) +#define ME4000_AO_SYNCHRONOUS_DISABLE _IO (ME4000_MAGIC, 47) + +#define ME4000_AO_EX_TRIG_TIMEOUT _IOW (ME4000_MAGIC, 48, unsigned long) +#define ME4000_AO_GET_FREE_BUFFER _IOR (ME4000_MAGIC, 49, unsigned long) + +#define ME4000_AI_GET_COUNT_BUFFER _IOR (ME4000_MAGIC, 50, unsigned long) + +#endif -- GitLab