Re: [PATCH 09/23] Staging: add me4000 pci data collection driver

From: Andrew Morton
Date: Wed Oct 15 2008 - 04:41:54 EST


> On Fri, 10 Oct 2008 15:42:33 -0700 Greg KH <greg@xxxxxxxxx> wrote:
> From: Greg Kroah-Hartman <gregkh@xxxxxxx>
>
> Originally written by Guenter Gebhardt <g.gebhardt@xxxxxxxxxxx>
>
> TODO:
> - checkpatch.pl cleanups
> - sparse cleanups
> - possible /proc interaction cleanups
> - more info needed for Kconfig entry
> - real device id?
> - module parameter cleanup

- sort includes (include/linux before asm/)

- Make me4000_board_info_list static

> Cc: Wolfgang Beiter <w.beiter@xxxxxx>
> Cc: Guenter Gebhardt <g.gebhardt@xxxxxxxxxxx>
> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>
> ---
> 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(+), 0 deletions(-)
> 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 6da7662..56c73bc 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 cd6d6a5..97df19b 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 0000000..5e6c9de
> --- /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 0000000..74487cd
> --- /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 0000000..bbb8386
> --- /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 <gregkh@xxxxxxx>
> +and Cc: Wolfgang Beiter <w.beiter@xxxxxx> and
> +Guenter Gebhardt <g.gebhardt@xxxxxxxxxxx>
> +
> diff --git a/drivers/staging/me4000/me4000.c b/drivers/staging/me4000/me4000.c
> new file mode 100644
> index 0000000..862dd7f
> --- /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@xxxxxxxxxxx)
> + *
> + * This file is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the 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 <g.gebhardt@xxxxxxxxxxx>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/fs.h>
> +#include <linux/sched.h>
> +#include <linux/interrupt.h>
> +#include <linux/pci.h>
> +#include <asm/io.h>
> +#include <asm/system.h>
> +#include <asm/uaccess.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/fs.h>
> +#include <linux/mm.h>
> +#include <linux/unistd.h>
> +#include <linux/list.h>
> +#include <linux/proc_fs.h>
> +
> +#include <linux/poll.h>
> +#include <linux/vmalloc.h>
> +#include <asm/pgtable.h>
> +#include <asm/uaccess.h>
> +#include <linux/types.h>

Reorder includes (inxlude/linux before include/asm)

> +#include <linux/slab.h>
> +
> +/* 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 <g.gebhardt@xxxxxxxxxxx>");
> +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);

static

> +/* 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;

s/= 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);

kill

> +/*-----------------------------------------------------------------------------
> + 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);

lots of these are unneeded.

> +/*-----------------------------------------------------------------------------
> + 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);

probably lots of those too.

> +/*-----------------------------------------------------------------------------
> + 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);

blah

> +/*-----------------------------------------------------------------------------
> + 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);

wtf

> +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);
> +}

yet another home-made circular buffer implementation

rename all struct foo_t to struct foo

> +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,
> +};

.foo = bar,

> +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,
> +};

dittoes

> +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
> +};

ditto

> +int __init me4000_init_module(void)

static

> +{
> + int result = 0;

unneeded initialisation

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

Yet another home-made prdebug?

> + ("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:

crazy label indenting

> + 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);
> + }
> +}

locking for me4000_board_info_list

> +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);

me4000_regbase and me4000_regbase_size should become resource_size_t.

> + 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);

dittoes

> + 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));

kzalloc

> + 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);

kzalloc

> + /* 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");

__func__(?)

> + if (ao_context->circ_buf.buf)
> + kfree(ao_context->circ_buf.buf);

kfree(NULL) is legal

> + 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);

etc

> + 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));

kzalloc()

> + 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));

etc

> + 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));

etc

> + 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));

ho hum

> + 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;
> + }

__func__

> + return 0;
> +
> + PROBE_ERROR_7:

indent

> + 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;

this doesn't do anything

> +
> + 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);

whee

> + }
> +
> + /* 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)) {

eh?

> + 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);

remove all sleep_on()s

> + 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);

kzalloc

> + /* 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;
> + }

erm. Exactly what is the userspace interface which this driver is
implementing?

> +#define ME4000_BOARD_VERSIONS (sizeof(me4000_boards) / sizeof(me4000_board_t) - 1)

Kill this altogether, use open-coded ARRAY_SIZE


<can't take any more, stops there>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/