Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

me4600_ao.c

Go to the documentation of this file.
/**
 * @file me4600_ao.c
 *
 * @brief ME-4000 analog output subdevice instance.
 * @note Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
 * @author Guenter Gebhardt
 * @author Krzysztof Gantzke  (k.gantzke@meilhaus.de)
 */

/*
 * Copyright (C) 2007 Meilhaus Electronic GmbH (support@meilhaus.de)
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef __KERNEL__
#  define __KERNEL__
#endif

///Common part. (For normal and Bosch builds.)

/* Includes
 */

#include <linux/module.h>

#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

#include "medefines.h"
#include "meinternal.h"
#include "meerror.h"

#include "medebug.h"
#include "meids.h"
#include "me4600_reg.h"
#include "me4600_ao_reg.h"
#include "me4600_ao.h"

/* Defines
 */

static int me4600_ao_query_range_by_min_max(me_subdevice_t *subdevice,
                                  int unit,
                                  int *min,
                                  int *max, int *maxdata, int *range);

static int me4600_ao_query_number_ranges(me_subdevice_t *subdevice,
                               int unit, int *count);

static int me4600_ao_query_range_info(me_subdevice_t *subdevice,
                              int range,
                              int *unit,
                              int *min, int *max, int *maxdata);

static int me4600_ao_query_timer(me_subdevice_t *subdevice,
                         int timer,
                         int *base_frequency,
                         long long *min_ticks, long long *max_ticks);

static int me4600_ao_query_number_channels(me_subdevice_t *subdevice,
                                 int *number);

static int me4600_ao_query_subdevice_type(me_subdevice_t *subdevice,
                                int *type, int *subtype);

static int me4600_ao_query_subdevice_caps(me_subdevice_t *subdevice,
                                int *caps);

static int me4600_ao_query_subdevice_caps_args(struct me_subdevice *subdevice,
                                     int cap, int *args, int count);

#ifndef BOSCH
/// @note NORMAL BUILD
/// @author Krzysztof Gantzke   (k.gantzke@meilhaus.de)
/* Includes
 */

# include <linux/workqueue.h>

/* Defines
 */

/** Remove subdevice.
*/
static void me4600_ao_destructor(struct me_subdevice *subdevice);

/** Reset subdevice. Stop all actions. Reset registry. Disable FIFO. Set output to 0V and status to 'none'.
*/
static int me4600_ao_io_reset_subdevice(me_subdevice_t *subdevice,
                              struct file *filep, int flags);

/** Set output as single
*/
static int me4600_ao_io_single_config(me_subdevice_t *subdevice,
                              struct file *filep,
                              int channel,
                              int single_config,
                              int ref,
                              int trig_chan,
                              int trig_type, int trig_edge, int flags);

/** Pass to user actual value of output.
*/
static int me4600_ao_io_single_read(me_subdevice_t *subdevice,
                            struct file *filep,
                            int channel,
                            int *value, int time_out, int flags);

/** Write to output requed value.
*/
static int me4600_ao_io_single_write(me_subdevice_t *subdevice,
                             struct file *filep,
                             int channel,
                             int value, int time_out, int flags);

/** Set output as streamed device.
*/
static int me4600_ao_io_stream_config(me_subdevice_t *subdevice,
                              struct file *filep,
                              meIOStreamConfig_t *config_list,
                              int count,
                              meIOStreamTrigger_t *trigger,
                              int fifo_irq_threshold, int flags);

/** Wait for / Check empty space in buffer.
*/
static int me4600_ao_io_stream_new_values(me_subdevice_t *subdevice,
                                struct file *filep,
                                int time_out, int *count, int flags);

/** Start streaming.
*/
static int me4600_ao_io_stream_start(me_subdevice_t *subdevice,
                             struct file *filep,
                             int start_mode, int time_out, int flags);

/** Check actual state. / Wait for end.
*/
static int me4600_ao_io_stream_status(me_subdevice_t *subdevice,
                              struct file *filep,
                              int wait,
                              int *status, int *values, int flags);

/** Stop streaming.
*/
static int me4600_ao_io_stream_stop(me_subdevice_t *subdevice,
                            struct file *filep,
                            int stop_mode, int flags);

/** Write datas to buffor.
*/
static int me4600_ao_io_stream_write(me_subdevice_t *subdevice,
                             struct file *filep,
                             int write_mode,
                             int *values, int *count, int flags);

/** Interrupt handler. Copy from buffer to FIFO.
*/
static irqreturn_t me4600_ao_isr(int irq, void *dev_id);
/** Copy data from circular buffer to fifo (fast) in wraparound mode.
*/
inline int ao_write_data_wraparound(me4600_ao_subdevice_t *instance, int count,
                            int start_pos);

/** Copy data from circular buffer to fifo (fast).
*/
inline int ao_write_data(me4600_ao_subdevice_t *instance, int count,
                   int start_pos);

/** Copy data from circular buffer to fifo (slow).
*/
inline int ao_write_data_pooling(me4600_ao_subdevice_t *instance, int count,
                         int start_pos);

/** Copy data from user space to circular buffer.
*/
inline int ao_get_data_from_user(me4600_ao_subdevice_t *instance, int count,
                         int *user_values);

/** Stop presentation. Preserve FIFOs.
*/
inline int ao_stop_immediately(me4600_ao_subdevice_t *instance);

/** Task for asynchronical state verifying.
*/
static void me4600_ao_work_control_task(struct work_struct *work);
/* Functions
 */

00209 static int me4600_ao_io_reset_subdevice(me_subdevice_t *subdevice,
                              struct file *filep, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      uint32_t tmp;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (flags) {
            PERROR("Invalid flag specified.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      ME_SUBDEVICE_ENTER;

      instance->status = ao_status_none;
      instance->ao_control_task_flag = 0;
      cancel_delayed_work(&instance->ao_control_task);
      instance->timeout.delay = 0;
      instance->timeout.start_time = jiffies;

      //Stop state machine.
      err = ao_stop_immediately(instance);

      //Remove from synchronous start.
      spin_lock(instance->preload_reg_lock);
      tmp = inl(instance->preload_reg);
      tmp &=
          ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->
            ao_idx);
      outl(tmp, instance->preload_reg);
      PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->preload_reg - instance->reg_base, tmp);
      *instance->preload_flags &=
          ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->
            ao_idx);
      spin_unlock(instance->preload_reg_lock);

      //Set single mode, dissable FIFO, dissable external trigger, set output to analog, block interrupt.
      outl(ME4600_AO_MODE_SINGLE | ME4600_AO_CTRL_BIT_STOP |
           ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_RESET_IRQ,
           instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base,
               ME4600_AO_MODE_SINGLE | ME4600_AO_CTRL_BIT_STOP |
               ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
               ME4600_AO_CTRL_BIT_RESET_IRQ);

      //Set output to 0V
      outl(0x8000, instance->single_reg);
      PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->single_reg - instance->reg_base, 0x8000);

      instance->circ_buf.head = 0;
      instance->circ_buf.tail = 0;
      instance->preloaded_count = 0;
      instance->data_count = 0;
      instance->single_value = 0x8000;
      instance->single_value_in_fifo = 0x8000;

      //Set status to signal that device is unconfigured.
      instance->status = ao_status_none;

      //Signal reset if user is on wait.
      wake_up_interruptible_all(&instance->wait_queue);

      ME_SUBDEVICE_EXIT;

      return err;
}

00283 static int me4600_ao_io_single_config(me_subdevice_t *subdevice,
                              struct file *filep,
                              int channel,
                              int single_config,
                              int ref,
                              int trig_chan,
                              int trig_type, int trig_edge, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      uint32_t ctrl;
      uint32_t sync;
      unsigned long cpu_flags;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      // Checking parameters
      if (flags) {
            PERROR
                ("Invalid flag specified. Must be ME_IO_SINGLE_CONFIG_NO_FLAGS.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      switch (trig_type) {
      case ME_TRIG_TYPE_SW:
            if (trig_edge != ME_TRIG_EDGE_NONE) {
                  PERROR
                      ("Invalid trigger edge. Software trigger has not edge.\n");
                  return ME_ERRNO_INVALID_TRIG_EDGE;
            }
            break;

      case ME_TRIG_TYPE_EXT_DIGITAL:
            switch (trig_edge) {
            case ME_TRIG_EDGE_ANY:
            case ME_TRIG_EDGE_RISING:
            case ME_TRIG_EDGE_FALLING:
                  break;

            default:
                  PERROR("Invalid trigger edge.\n");
                  return ME_ERRNO_INVALID_TRIG_EDGE;
            }
            break;

      default:
            PERROR
                ("Invalid trigger type. Trigger must be software or digital.\n");
            return ME_ERRNO_INVALID_TRIG_TYPE;
      }

      if ((trig_chan != ME_TRIG_CHAN_DEFAULT)
          && (trig_chan != ME_TRIG_CHAN_SYNCHRONOUS)) {
            PERROR("Invalid trigger channel specified.\n");
            return ME_ERRNO_INVALID_TRIG_CHAN;
      }

      if (ref != ME_REF_AO_GROUND) {
            PERROR
                ("Invalid reference. Analog outputs have to have got REF_AO_GROUND.\n");
            return ME_ERRNO_INVALID_REF;
      }

      if (single_config != 0) {
            PERROR
                ("Invalid single config specified. Only one range for anlog outputs is available.\n");
            return ME_ERRNO_INVALID_SINGLE_CONFIG;
      }

      if (channel != 0) {
            PERROR
                ("Invalid channel number specified. Analog output have only one channel.\n");
            return ME_ERRNO_INVALID_CHANNEL;
      }

      ME_SUBDEVICE_ENTER;

      //Subdevice running in stream mode!
      if ((instance->status >= ao_status_stream_run_wait)
          && (instance->status < ao_status_stream_end)) {
            PERROR("Subdevice is busy.\n");
            ME_SUBDEVICE_EXIT;

            return ME_ERRNO_SUBDEVICE_BUSY;
      }
/// @note For single all calls (config and write) are erasing previous state!

      instance->status = ao_status_none;

      // Correct single mirrors
      instance->single_value_in_fifo = instance->single_value;

      //Stop device
      err = ao_stop_immediately(instance);
      if (err) {
            PERROR_CRITICAL("FSM IS BUSY!\n");
            ME_SUBDEVICE_EXIT;

            return ME_ERRNO_SUBDEVICE_BUSY;
      }
      // Set control register.
      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
      // Set stop bit. Stop streaming mode.
      ctrl = inl(instance->ctrl_reg);
      //Reset all bits.
      ctrl = ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP;

      if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL) {
            PINFO("External digital trigger.\n");

            if (trig_edge == ME_TRIG_EDGE_ANY) {
//                              ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
                  instance->ctrl_trg =
                      ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
                      ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
            } else if (trig_edge == ME_TRIG_EDGE_FALLING) {
//                              ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
                  instance->ctrl_trg = ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
            } else if (trig_edge == ME_TRIG_EDGE_RISING) {
                  instance->ctrl_trg = 0x0;
            }
      } else if (trig_type == ME_TRIG_TYPE_SW) {
            PDEBUG("Software trigger\n");
            instance->ctrl_trg = 0x0;
      }

      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base, ctrl);
      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      // Set preload/synchronization register.
      spin_lock(instance->preload_reg_lock);
      if (trig_type == ME_TRIG_TYPE_SW) {
            *instance->preload_flags &=
                ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx);
      } else                  //if (trig_type == ME_TRIG_TYPE_EXT_DIGITAL)
      {
            *instance->preload_flags |=
                ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx;
      }

      if (trig_chan == ME_TRIG_CHAN_DEFAULT) {
            *instance->preload_flags &=
                ~(ME4600_AO_SYNC_HOLD << instance->ao_idx);
      } else                  //if (trig_chan == ME_TRIG_CHAN_SYNCHRONOUS)
      {
            *instance->preload_flags |=
                ME4600_AO_SYNC_HOLD << instance->ao_idx;
      }

      //Reset hardware register
      sync = inl(instance->preload_reg);
      PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->preload_reg - instance->reg_base, sync);
      sync &= ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx);
      sync |= ME4600_AO_SYNC_HOLD << instance->ao_idx;

      //Output configured in default (safe) mode.
      outl(sync, instance->preload_reg);
      PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->preload_reg - instance->reg_base, sync);
      spin_unlock(instance->preload_reg_lock);

      instance->status = ao_status_single_configured;

      ME_SUBDEVICE_EXIT;

      return err;
}

00456 static int me4600_ao_io_single_read(me_subdevice_t *subdevice,
                            struct file *filep,
                            int channel,
                            int *value, int time_out, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;

      unsigned long j;
      unsigned long delay = 0;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (flags & ~ME_IO_SINGLE_NONBLOCKING) {
            PERROR("Invalid flag specified. %d\n", flags);
            return ME_ERRNO_INVALID_FLAGS;
      }

      if (time_out < 0) {
            PERROR("Invalid timeout specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      if (channel != 0) {
            PERROR("Invalid channel number specified.\n");
            return ME_ERRNO_INVALID_CHANNEL;
      }

      if ((instance->status >= ao_status_stream_configured)
          && (instance->status <= ao_status_stream_end)) {
            PERROR("Subdevice not configured to work in single mode!\n");
            return ME_ERRNO_PREVIOUS_CONFIG;
      }

      ME_SUBDEVICE_ENTER;
      if ((!flags) && (instance->status == ao_status_single_run_wait)) {      //Blocking mode. Wait for trigger.
            if (time_out) {
                  delay = (time_out * HZ) / 1000;
                  if (delay == 0)
                        delay = 1;
            }

            j = jiffies;

            //Only runing process will interrupt this call. Events are signaled when status change. This procedure has own timeout.
            wait_event_interruptible_timeout(instance->wait_queue,
                                     (instance->status !=
                                      ao_status_single_run_wait),
                                     (delay) ? delay +
                                     1 : LONG_MAX);

            if (instance->status == ao_status_none) {
                  PDEBUG("Single canceled.\n");
                  err = ME_ERRNO_CANCELLED;
            }

            if (signal_pending(current)) {
                  PERROR("Wait on start of state machine interrupted.\n");
                  instance->status = ao_status_none;
                  ao_stop_immediately(instance);
                  err = ME_ERRNO_SIGNAL;
            }

            if ((delay) && ((jiffies - j) >= delay)) {

                  PDEBUG("Timeout reached.\n");
                  err = ME_ERRNO_TIMEOUT;
            }

            *value =
                (!err) ? instance->single_value_in_fifo : instance->
                single_value;
      } else {          //Non-blocking mode
            //Read value
            *value = instance->single_value;
      }

      ME_SUBDEVICE_EXIT;

      return err;
}

00540 static int me4600_ao_io_single_write(me_subdevice_t *subdevice,
                             struct file *filep,
                             int channel,
                             int value, int time_out, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      unsigned long cpu_flags;
      unsigned long j;
      unsigned long delay = 0x0;

      //Registry handling variables.
      uint32_t sync_mask;
      uint32_t mode;
      uint32_t tmp;
      uint32_t ctrl;
      uint32_t status;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (flags &
          ~(ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS |
            ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
            PERROR("Invalid flag specified.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if (time_out < 0) {
            PERROR("Invalid timeout specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      if (value & ~ME4600_AO_MAX_DATA) {
            PERROR("Invalid value provided.\n");
            return ME_ERRNO_VALUE_OUT_OF_RANGE;
      }

      if (channel != 0) {
            PERROR("Invalid channel number specified.\n");
            return ME_ERRNO_INVALID_CHANNEL;
      }

      if ((instance->status == ao_status_none)
          || (instance->status > ao_status_single_end)) {
            PERROR("Subdevice not configured to work in single mode!\n");
            return ME_ERRNO_PREVIOUS_CONFIG;
      }

      ME_SUBDEVICE_ENTER;

/// @note For single all calls (config and write) are erasing previous state!

      //Cancel control task
      PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
      instance->ao_control_task_flag = 0;
      cancel_delayed_work(&instance->ao_control_task);

      // Correct single mirrors
      instance->single_value_in_fifo = instance->single_value;

      //Stop device
      err = ao_stop_immediately(instance);
      if (err) {
            PERROR_CRITICAL("FSM IS BUSY!\n");
            ME_SUBDEVICE_EXIT;

            return ME_ERRNO_SUBDEVICE_BUSY;
      }

      if (time_out) {
            delay = (time_out * HZ) / 1000;

            if (delay == 0)
                  delay = 1;
      }

      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);

      instance->single_value_in_fifo = value;

      ctrl = inl(instance->ctrl_reg);

      if (!instance->fifo) {  //No FIFO
            //Set the single mode.
            ctrl &= ~ME4600_AO_CTRL_MODE_MASK;

            //Write value
            PDEBUG("Write value\n");
            outl(value, instance->single_reg);
            PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->single_reg - instance->reg_base, value);
      } else {          // mix-mode
            //Set speed
            outl(ME4600_AO_MIN_CHAN_TICKS - 1, instance->timer_reg);
            PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->timer_reg - instance->reg_base,
                     (int)ME4600_AO_MIN_CHAN_TICKS);
            instance->hardware_stop_delay = HZ / 10;  //100ms

            status = inl(instance->status_reg);

            //Set the continous mode.
            ctrl &= ~ME4600_AO_CTRL_MODE_MASK;
            ctrl |= ME4600_AO_MODE_CONTINUOUS;

            //Prepare FIFO
            if (!(ctrl & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it.
                  PINFO("Enableing FIFO.\n");
                  ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  ctrl |=
                      ME4600_AO_CTRL_BIT_ENABLE_FIFO |
                      ME4600_AO_CTRL_BIT_RESET_IRQ;
            } else {    //Check if FIFO is empty
                  if (status & ME4600_AO_STATUS_BIT_EF) {   //FIFO not empty
                        PINFO("Reseting FIFO.\n");
                        ctrl &=
                            ~(ME4600_AO_CTRL_BIT_ENABLE_FIFO |
                              ME4600_AO_CTRL_BIT_ENABLE_IRQ);
                        ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
                        outl(ctrl, instance->ctrl_reg);
                        PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                                 instance->reg_base,
                                 instance->ctrl_reg -
                                 instance->reg_base, ctrl);

                        ctrl |=
                            ME4600_AO_CTRL_BIT_ENABLE_FIFO |
                            ME4600_AO_CTRL_BIT_RESET_IRQ;
                  } else {    //FIFO empty, only interrupt needs to be disabled!
                        ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                        ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
                  }
            }

            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);

            //Write output - 1 value to FIFO
            if (instance->ao_idx & 0x1) {
                  outl(value <<= 16, instance->fifo_reg);
                  PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->fifo_reg - instance->reg_base,
                           value <<= 16);
            } else {
                  outl(value, instance->fifo_reg);
                  PDEBUG_REG("fifo_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->fifo_reg - instance->reg_base,
                           value);
            }
      }

      mode = *instance->preload_flags >> instance->ao_idx;
      mode &= (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG);

      PINFO("Triggering mode: 0x%x\n", mode);

      spin_lock(instance->preload_reg_lock);
      sync_mask = inl(instance->preload_reg);
      PDEBUG_REG("preload_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->preload_reg - instance->reg_base, sync_mask);
      switch (mode) {
      case 0:           //Individual software
            ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;

            if (!instance->fifo) {  // No FIFO - In this case resetting 'ME4600_AO_SYNC_HOLD' will trigger output.
                  if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) { //Now we can set correct mode. This is exception. It is set to synchronous and triggered later.
                        sync_mask &=
                            ~(ME4600_AO_SYNC_EXT_TRIG << instance->
                              ao_idx);
                        sync_mask |=
                            ME4600_AO_SYNC_HOLD << instance->ao_idx;

                        outl(sync_mask, instance->preload_reg);
                        PDEBUG_REG
                            ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                             instance->reg_base,
                             instance->preload_reg - instance->reg_base,
                             sync_mask);
                  }
            } else {    // FIFO
                  if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) {     //Now we can set correct mode.
                        sync_mask &=
                            ~((ME4600_AO_SYNC_EXT_TRIG |
                               ME4600_AO_SYNC_HOLD) << instance->
                              ao_idx);

                        outl(sync_mask, instance->preload_reg);
                        PDEBUG_REG
                            ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                             instance->reg_base,
                             instance->preload_reg - instance->reg_base,
                             sync_mask);
                  }
            }
            instance->single_value = value;
            break;

      case ME4600_AO_SYNC_EXT_TRIG: //Individual hardware
            ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;

            if (!instance->fifo) {  // No FIFO - In this case resetting 'ME4600_AO_SYNC_HOLD' will trigger output.
                  if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD) { //Now we can set correct mode
                        sync_mask &=
                            ~(ME4600_AO_SYNC_EXT_TRIG << instance->
                              ao_idx);
                        sync_mask |=
                            ME4600_AO_SYNC_HOLD << instance->ao_idx;

                        outl(sync_mask, instance->preload_reg);
                        PDEBUG_REG
                            ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                             instance->reg_base,
                             instance->preload_reg - instance->reg_base,
                             sync_mask);
                  }
            } else {    // FIFO
                  if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != 0x0) {     //Now we can set correct mode.
                        sync_mask &=
                            ~((ME4600_AO_SYNC_EXT_TRIG |
                               ME4600_AO_SYNC_HOLD) << instance->
                              ao_idx);

                        outl(sync_mask, instance->preload_reg);
                        PDEBUG_REG
                            ("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                             instance->reg_base,
                             instance->preload_reg - instance->reg_base,
                             sync_mask);
                  }
            }
            break;

      case ME4600_AO_SYNC_HOLD:     //Synchronous software
            ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;

//                                      if((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != ME4600_AO_SYNC_HOLD)
            if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG)) {   //Now we can set correct mode
                  sync_mask |=
                      ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx;
//                                              sync_mask &= ~(ME4600_AO_SYNC_EXT_TRIG << instance->ao_idx);
                  sync_mask |= ME4600_AO_SYNC_HOLD << instance->ao_idx;

                  outl(sync_mask, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask);
            }
            break;

      case (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG): //Synchronous hardware
            ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
            if ((sync_mask & ((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->ao_idx)) != (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG)) {   //Now we can set correct mode
                  sync_mask |=
                      (ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) <<
                      instance->ao_idx;

                  outl(sync_mask, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask);
            }
            break;
      }
//              spin_unlock(instance->preload_reg_lock);        // Moved down.

      //Activate ISM (remove 'stop' bits)
      ctrl &=
          ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
            ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
      ctrl |= instance->ctrl_trg;
      ctrl &= ~(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base, ctrl);
      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

/// @note When flag 'ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS' is set than output is triggered. ALWAYS!

      if (!instance->fifo) {  //No FIFO
            if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {     //Fired all software synchronous outputs.
                  tmp = ~(*instance->preload_flags | 0xFFFF0000);
                  PINFO
                      ("Fired all software synchronous outputs. mask:0x%08x\n",
                       tmp);
                  tmp |= sync_mask & 0xFFFF0000;
                  // Add this channel to list
                  tmp &= ~(ME4600_AO_SYNC_HOLD << instance->ao_idx);

                  //Fire
                  PINFO("Software trigger.\n");
                  outl(tmp, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           tmp);

                  //Restore save settings
                  outl(sync_mask, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask);
            } else if (!mode) {     // Add this channel to list
                  outl(sync_mask &
                       ~(ME4600_AO_SYNC_HOLD << instance->ao_idx),
                       instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask & ~(ME4600_AO_SYNC_HOLD <<
                                     instance->ao_idx));

                  //Fire
                  PINFO("Software trigger.\n");

                  //Restore save settings
                  outl(sync_mask, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask);
            }

      } else {          // mix-mode - begin
            if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {     //Trigger outputs
                  //Add channel to start list
                  outl(sync_mask |
                       (ME4600_AO_SYNC_HOLD << instance->ao_idx),
                       instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask | (ME4600_AO_SYNC_HOLD <<
                                    instance->ao_idx));

                  //Fire
                  PINFO
                      ("Fired all software synchronous outputs by software trigger.\n");
                  outl(0x8000, instance->single_reg);
                  PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->single_reg - instance->reg_base,
                           0x8000);

                  //Restore save settings
                  outl(sync_mask, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           sync_mask);
            } else if (!mode) {     //Trigger outputs
/*                //Remove channel from start list //<== Unnecessary. Removed.
                  outl(sync_mask & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, tmp);
*/
                  //Fire
                  PINFO("Software trigger.\n");
                  outl(0x8000, instance->single_reg);
                  PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->single_reg - instance->reg_base,
                           0x8000);

/*                //Restore save settings //<== Unnecessary. Removed.
                  outl(sync_mask, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, sync_mask);
*/
            }
      }
      spin_unlock(instance->preload_reg_lock);

      j = jiffies;
      instance->status = ao_status_single_run_wait;

      instance->timeout.delay = delay;
      instance->timeout.start_time = j;
      instance->ao_control_task_flag = 1;
      queue_delayed_work(instance->me4600_workqueue,
                     &instance->ao_control_task, 1);

      if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {

            //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
            wait_event_interruptible_timeout(instance->wait_queue,
                                     (instance->status !=
                                      ao_status_single_run_wait),
                                     (delay) ? delay +
                                     1 : LONG_MAX);

            if (((!delay) || ((jiffies - j) <= delay))
                && (instance->status != ao_status_single_end)) {
                  PDEBUG("Single canceled.\n");
                  err = ME_ERRNO_CANCELLED;
            }

            if (signal_pending(current)) {
                  PERROR("Wait on start of state machine interrupted.\n");
                  instance->ao_control_task_flag = 0;
                  cancel_delayed_work(&instance->ao_control_task);
                  ao_stop_immediately(instance);
                  instance->status = ao_status_none;
                  err = ME_ERRNO_SIGNAL;
            }

            if ((delay) && ((jiffies - j) >= delay)) {
                  if (instance->status == ao_status_single_end) {
                        PDEBUG("Timeout reached.\n");
                  } else {
                        if ((jiffies - j) > delay) {
                              PERROR
                                  ("Timeout reached. Not handled by control task!\n");
                        } else {
                              PERROR
                                  ("Timeout reached. Signal come but status is strange: %d\n",
                                   instance->status);
                        }

                        ao_stop_immediately(instance);
                  }

                  instance->ao_control_task_flag = 0;
                  cancel_delayed_work(&instance->ao_control_task);
                  instance->status = ao_status_single_end;
                  err = ME_ERRNO_TIMEOUT;
            }
      }

      ME_SUBDEVICE_EXIT;

      return err;
}

00982 static int me4600_ao_io_stream_config(me_subdevice_t *subdevice,
                              struct file *filep,
                              meIOStreamConfig_t *config_list,
                              int count,
                              meIOStreamTrigger_t *trigger,
                              int fifo_irq_threshold, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      uint32_t ctrl;
      unsigned long cpu_flags;
      uint64_t conv_ticks;
      unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow;
      unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      conv_ticks =
          (uint64_t) conv_start_ticks_low +
          ((uint64_t) conv_start_ticks_high << 32);

      if (flags &
          ~(ME_IO_STREAM_CONFIG_HARDWARE_ONLY | ME_IO_STREAM_CONFIG_WRAPAROUND
            | ME_IO_STREAM_CONFIG_BIT_PATTERN)) {
            PERROR("Invalid flags.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) {
            if (!(flags & ME_IO_STREAM_CONFIG_WRAPAROUND)) {
                  PERROR
                      ("Hardware ME_IO_STREAM_CONFIG_HARDWARE_ONLY has to be with ME_IO_STREAM_CONFIG_WRAPAROUND.\n");
                  return ME_ERRNO_INVALID_FLAGS;
            }

            if ((trigger->iAcqStopTrigType != ME_TRIG_TYPE_NONE)
                || (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE)) {
                  PERROR
                      ("Hardware wraparound mode must be in infinite mode.\n");
                  return ME_ERRNO_INVALID_FLAGS;
            }
      }

      if (count != 1) {
            PERROR("Only 1 entry in config list acceptable.\n");
            return ME_ERRNO_INVALID_CONFIG_LIST_COUNT;
      }

      if (config_list[0].iChannel != 0) {
            PERROR("Invalid channel number specified.\n");
            return ME_ERRNO_INVALID_CHANNEL;
      }

      if (config_list[0].iStreamConfig != 0) {
            PERROR("Only one range available.\n");
            return ME_ERRNO_INVALID_STREAM_CONFIG;
      }

      if (config_list[0].iRef != ME_REF_AO_GROUND) {
            PERROR("Output is referenced to ground.\n");
            return ME_ERRNO_INVALID_REF;
      }

      if ((trigger->iAcqStartTicksLow != 0)
          || (trigger->iAcqStartTicksHigh != 0)) {
            PERROR
                ("Invalid acquisition start trigger argument specified.\n");
            return ME_ERRNO_INVALID_ACQ_START_ARG;
      }

      if (config_list[0].iFlags) {
            PERROR("Invalid config list flag.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      switch (trigger->iAcqStartTrigType) {
      case ME_TRIG_TYPE_SW:
            if (trigger->iAcqStartTrigEdge != ME_TRIG_EDGE_NONE) {
                  PERROR
                      ("Invalid acquisition start trigger edge specified.\n");
                  return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
            }
            break;

      case ME_TRIG_TYPE_EXT_DIGITAL:
            switch (trigger->iAcqStartTrigEdge) {
            case ME_TRIG_EDGE_ANY:
            case ME_TRIG_EDGE_RISING:
            case ME_TRIG_EDGE_FALLING:
                  break;

            default:
                  PERROR
                      ("Invalid acquisition start trigger edge specified.\n");
                  return ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;
            }
            break;

      default:
            PERROR("Invalid acquisition start trigger type specified.\n");
            return ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;
      }

      if (trigger->iScanStartTrigType != ME_TRIG_TYPE_FOLLOW) {
            PERROR("Invalid scan start trigger type specified.\n");
            return ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;
      }

      if (trigger->iConvStartTrigType != ME_TRIG_TYPE_TIMER) {
            PERROR("Invalid conv start trigger type specified.\n");
            return ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;
      }

      if ((conv_ticks < ME4600_AO_MIN_CHAN_TICKS)
          || (conv_ticks > ME4600_AO_MAX_CHAN_TICKS)) {
            PERROR("Invalid conv start trigger argument specified.\n");
            return ME_ERRNO_INVALID_CONV_START_ARG;
      }

      if (trigger->iAcqStartTicksLow || trigger->iAcqStartTicksHigh) {
            PERROR("Invalid acq start trigger argument specified.\n");
            return ME_ERRNO_INVALID_ACQ_START_ARG;
      }

      if (trigger->iScanStartTicksLow || trigger->iScanStartTicksHigh) {
            PERROR("Invalid scan start trigger argument specified.\n");
            return ME_ERRNO_INVALID_SCAN_START_ARG;
      }

      switch (trigger->iScanStopTrigType) {
      case ME_TRIG_TYPE_NONE:
            if (trigger->iScanStopCount != 0) {
                  PERROR("Invalid scan stop count specified.\n");
                  return ME_ERRNO_INVALID_SCAN_STOP_ARG;
            }
            break;

      case ME_TRIG_TYPE_COUNT:
            if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
                  if (trigger->iScanStopCount <= 0) {
                        PERROR("Invalid scan stop count specified.\n");
                        return ME_ERRNO_INVALID_SCAN_STOP_ARG;
                  }
            } else {
                  PERROR("The continous mode has not 'scan' contects.\n");
                  return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
            }
            break;

      default:
            PERROR("Invalid scan stop trigger type specified.\n");
            return ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE;
      }

      switch (trigger->iAcqStopTrigType) {
      case ME_TRIG_TYPE_NONE:
            if (trigger->iAcqStopCount != 0) {
                  PERROR("Invalid acq stop count specified.\n");
                  return ME_ERRNO_INVALID_ACQ_STOP_ARG;
            }
            break;

      case ME_TRIG_TYPE_COUNT:
            if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
                  PERROR("Invalid acq stop trigger type specified.\n");
                  return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
            }

            if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
                  if (trigger->iAcqStopCount <= 0) {
                        PERROR
                            ("The continous mode has not 'scan' contects.\n");
                        return ME_ERRNO_INVALID_ACQ_STOP_ARG;
                  }
            }
            break;

      default:
            PERROR("Invalid acq stop trigger type specified.\n");
            return ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
      }

      switch (trigger->iAcqStartTrigChan) {
      case ME_TRIG_CHAN_DEFAULT:
      case ME_TRIG_CHAN_SYNCHRONOUS:
            break;

      default:
            PERROR("Invalid acq start trigger channel specified.\n");
            return ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN;
      }

      ME_SUBDEVICE_ENTER;

      if ((flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) && !instance->bitpattern) {
            PERROR("This subdevice not support output redirection.\n");
            ME_SUBDEVICE_EXIT;
            return ME_ERRNO_INVALID_FLAGS;
      }
      //Stop device

      //Cancel control task
      PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
      instance->ao_control_task_flag = 0;
      cancel_delayed_work(&instance->ao_control_task);

      //Check if state machine is stopped.
      err = ao_stop_immediately(instance);
      if (err) {
            PERROR_CRITICAL("FSM IS BUSY!\n");
            ME_SUBDEVICE_EXIT;

            return ME_ERRNO_SUBDEVICE_BUSY;
      }

      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
      //Reset control register. Block all actions. Disable IRQ. Disable FIFO.
      ctrl =
          ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP |
          ME4600_AO_CTRL_BIT_RESET_IRQ;
      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base, ctrl);

      //This is paranoic, but to be sure.
      instance->preloaded_count = 0;
      instance->data_count = 0;
      instance->circ_buf.head = 0;
      instance->circ_buf.tail = 0;

      /* Set mode. */
      if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {   //Wraparound
            if (flags & ME_IO_STREAM_CONFIG_HARDWARE_ONLY) {      //Hardware wraparound
                  PINFO("Hardware wraparound.\n");
                  ctrl |= ME4600_AO_MODE_WRAPAROUND;
                  instance->mode = ME4600_AO_HW_WRAP_MODE;
            } else {    //Software wraparound
                  PINFO("Software wraparound.\n");
                  ctrl |= ME4600_AO_MODE_CONTINUOUS;
                  instance->mode = ME4600_AO_SW_WRAP_MODE;
            }
      } else {          //Continous
            PINFO("Continous.\n");
            ctrl |= ME4600_AO_MODE_CONTINUOUS;
            instance->mode = ME4600_AO_CONTINOUS;
      }

      //Set the trigger edge.
      if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {     //Set the trigger type and edge for external trigger.
            PINFO("External digital trigger.\n");
            instance->start_mode = ME4600_AO_EXT_TRIG;
/*
                  ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
*/
            switch (trigger->iAcqStartTrigEdge) {
            case ME_TRIG_EDGE_RISING:
                  PINFO("Set the trigger edge: rising.\n");
                  instance->ctrl_trg = 0x0;
                  break;

            case ME_TRIG_EDGE_FALLING:
                  PINFO("Set the trigger edge: falling.\n");
//                                      ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
                  instance->ctrl_trg = ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
                  break;

            case ME_TRIG_EDGE_ANY:
                  PINFO("Set the trigger edge: both edges.\n");
//                                      ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE | ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
                  instance->ctrl_trg =
                      ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
                      ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
                  break;
            }
      } else {
            PINFO("Internal software trigger.\n");
            instance->start_mode = 0;
      }

      //Set the stop mode and value.
      if (trigger->iAcqStopTrigType == ME_TRIG_TYPE_COUNT) {      //Amount of data
            instance->stop_mode = ME4600_AO_ACQ_STOP_MODE;
            instance->stop_count = trigger->iAcqStopCount;
      } else if (trigger->iScanStopTrigType == ME_TRIG_TYPE_COUNT) {    //Amount of 'scans'
            instance->stop_mode = ME4600_AO_SCAN_STOP_MODE;
            instance->stop_count = trigger->iScanStopCount;
      } else {          //Infinite
            instance->stop_mode = ME4600_AO_INF_STOP_MODE;
            instance->stop_count = 0;
      }

      PINFO("Stop count: %d.\n", instance->stop_count);

      if (trigger->iAcqStartTrigChan == ME_TRIG_CHAN_SYNCHRONOUS) {     //Synchronous start
            instance->start_mode |= ME4600_AO_SYNC_HOLD;
            if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_EXT_DIGITAL) {     //Externaly triggered
                  PINFO("Synchronous start. Externaly trigger active.\n");
                  instance->start_mode |= ME4600_AO_SYNC_EXT_TRIG;
            }
#ifdef MEDEBUG_INFO
            else {
                  PINFO
                      ("Synchronous start. Externaly trigger dissabled.\n");
            }
#endif

      }
      //Set speed
      outl(conv_ticks - 2, instance->timer_reg);
      PDEBUG_REG("timer_reg outl(0x%lX+0x%lX)=0x%llx\n", instance->reg_base,
               instance->timer_reg - instance->reg_base, conv_ticks - 2);
      instance->hardware_stop_delay = (int)(conv_ticks * HZ) / ME4600_AO_BASE_FREQUENCY;  //<== MUST be with cast!

      //Conect outputs to analog or digital port.
      if (flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) {
            ctrl |= ME4600_AO_CTRL_BIT_ENABLE_DO;
      }
      // Write the control word
      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base, ctrl);

      //Set status.
      instance->status = ao_status_stream_configured;
      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      ME_SUBDEVICE_EXIT;

      return err;
}

01320 static int me4600_ao_io_stream_new_values(me_subdevice_t *subdevice,
                                struct file *filep,
                                int time_out, int *count, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      long t = 0;
      long j;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      if (flags) {
            PERROR("Invalid flag specified.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if (!instance->circ_buf.buf) {
            PERROR("Circular buffer not exists.\n");
            return ME_ERRNO_INTERNAL;
      }

      if (time_out < 0) {
            PERROR("Invalid time_out specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      ME_SUBDEVICE_ENTER;

      if (me_circ_buf_space(&instance->circ_buf)) {   //The buffer is NOT full.
            *count = me_circ_buf_space(&instance->circ_buf);
      } else {          //The buffer is full.
            if (time_out) {
                  t = (time_out * HZ) / 1000;

                  if (t == 0)
                        t = 1;
            } else {    //Max time.
                  t = LONG_MAX;
            }

            *count = 0;

            j = jiffies;

            //Only runing process will interrupt this call. Interrupts are when FIFO HF is signaled.
            wait_event_interruptible_timeout(instance->wait_queue,
                                     ((me_circ_buf_space
                                       (&instance->circ_buf))
                                      || !(inl(instance->status_reg)
                                           &
                                           ME4600_AO_STATUS_BIT_FSM)),
                                     t);

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
                  PERROR("AO subdevice is not running.\n");
                  err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
            } else if (signal_pending(current)) {
                  PERROR("Wait on values interrupted from signal.\n");
                  instance->status = ao_status_none;
                  ao_stop_immediately(instance);
                  err = ME_ERRNO_SIGNAL;
            } else if ((jiffies - j) >= t) {
                  PERROR("Wait on values timed out.\n");
                  err = ME_ERRNO_TIMEOUT;
            } else {    //Uff... all is good. Inform user about empty space.
                  *count = me_circ_buf_space(&instance->circ_buf);
            }
      }

      ME_SUBDEVICE_EXIT;

      return err;
}

01401 static int me4600_ao_io_stream_start(me_subdevice_t *subdevice,
                             struct file *filep,
                             int start_mode, int time_out, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      unsigned long cpu_flags = 0;
      uint32_t status;
      uint32_t ctrl;
      uint32_t synch;
      int count = 0;
      int circ_buffer_count;

      unsigned long ref;
      unsigned long delay = 0;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      if (flags & ~ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
            PERROR("Invalid flags.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if (time_out < 0) {
            PERROR("Invalid timeout specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      if ((start_mode != ME_START_MODE_BLOCKING)
          && (start_mode != ME_START_MODE_NONBLOCKING)) {
            PERROR("Invalid start mode specified.\n");
            return ME_ERRNO_INVALID_START_MODE;
      }

      if (time_out) {
            delay = (time_out * HZ) / 1000;
            if (delay == 0)
                  delay = 1;
      }

      switch (instance->status) {   //Checking actual mode.
      case ao_status_stream_configured:
      case ao_status_stream_end:
            //Correct modes!
            break;

            //The device is in wrong mode.
      case ao_status_none:
      case ao_status_single_configured:
      case ao_status_single_run_wait:
      case ao_status_single_run:
      case ao_status_single_end_wait:
            PERROR
                ("Subdevice must be preinitialize correctly for streaming.\n");
            return ME_ERRNO_PREVIOUS_CONFIG;

      case ao_status_stream_fifo_error:
      case ao_status_stream_buffer_error:
      case ao_status_stream_error:
            PDEBUG("Before restart broke stream 'STOP' must be caled.\n");
            return ME_STATUS_ERROR;

      case ao_status_stream_run_wait:
      case ao_status_stream_run:
      case ao_status_stream_end_wait:
            PDEBUG("Stream is already working.\n");
            return ME_ERRNO_SUBDEVICE_BUSY;

      default:
            instance->status = ao_status_stream_error;
            PERROR_CRITICAL("Status is in wrong state!\n");
            return ME_ERRNO_INTERNAL;

      }

      ME_SUBDEVICE_ENTER;

      if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
            instance->circ_buf.tail += instance->preloaded_count;
            instance->circ_buf.tail &= instance->circ_buf.mask;
      }
      circ_buffer_count = me_circ_buf_values(&instance->circ_buf);

      if (!circ_buffer_count && !instance->preloaded_count) {     //No values in buffer
            ME_SUBDEVICE_EXIT;
            PERROR("No values in buffer!\n");
            return ME_ERRNO_LACK_OF_RESOURCES;
      }

      //Cancel control task
      PDEBUG("Cancel control task. idx=%d\n", instance->ao_idx);
      instance->ao_control_task_flag = 0;
      cancel_delayed_work(&instance->ao_control_task);

      //Stop device
      err = ao_stop_immediately(instance);
      if (err) {
            PERROR_CRITICAL("FSM IS BUSY!\n");
            ME_SUBDEVICE_EXIT;

            return ME_ERRNO_SUBDEVICE_BUSY;
      }
      //Set values for single_read()
      instance->single_value = ME4600_AO_MAX_DATA + 1;
      instance->single_value_in_fifo = ME4600_AO_MAX_DATA + 1;

      //Setting stop points
      if (instance->stop_mode == ME4600_AO_SCAN_STOP_MODE) {
            instance->stop_data_count =
                instance->stop_count * circ_buffer_count;
      } else {
            instance->stop_data_count = instance->stop_count;
      }

      if ((instance->stop_data_count != 0)
          && (instance->stop_data_count < circ_buffer_count)) {
            PERROR("More data in buffer than previously set limit!\n");
      }

      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
      ctrl = inl(instance->ctrl_reg);
      //Check FIFO
      if (!(ctrl & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) { //FIFO wasn't enabeled. Do it. <= This should be done by user call with ME_WRITE_MODE_PRELOAD
            PINFO("Enableing FIFO.\n");
            ctrl |=
                ME4600_AO_CTRL_BIT_ENABLE_FIFO |
                ME4600_AO_CTRL_BIT_RESET_IRQ;

            instance->preloaded_count = 0;
            instance->data_count = 0;
      } else {          //Block IRQ
            ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
      }
      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base,
               ctrl | ME4600_AO_CTRL_BIT_RESET_IRQ);

      //Fill FIFO <= Generaly this should be done by user pre-load call but this is second place to do it.
      status = inl(instance->status_reg);
      if (!(status & ME4600_AO_STATUS_BIT_EF)) {      //FIFO empty
            if (instance->stop_data_count == 0) {
                  count = ME4600_AO_FIFO_COUNT;
            } else {
                  count =
                      (ME4600_AO_FIFO_COUNT <
                       instance->
                       stop_data_count) ? ME4600_AO_FIFO_COUNT :
                      instance->stop_data_count;
            }

            //Copy data
            count =
                ao_write_data(instance, count, instance->preloaded_count);

            if (count < 0) {  //This should never happend!
                  PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
                  ME_SUBDEVICE_EXIT;
                  return ME_ERRNO_INTERNAL;
            }
      }
      //Set pre-load features.
      spin_lock(instance->preload_reg_lock);
      synch = inl(instance->preload_reg);
      synch &=
          ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) << instance->
            ao_idx);
      synch |=
          (instance->start_mode & ~ME4600_AO_EXT_TRIG) << instance->ao_idx;
      outl(synch, instance->preload_reg);
      PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->preload_reg - instance->reg_base, synch);
      spin_unlock(instance->preload_reg_lock);

      //Default count is '0'
      if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
            instance->preloaded_count = 0;
            instance->circ_buf.tail += count;
            instance->circ_buf.tail &= instance->circ_buf.mask;
      } else {          //Wraparound
            instance->preloaded_count += count;
            instance->data_count += count;

            //Special case: Infinite wraparound with less than FIFO datas always should runs in hardware mode.
            if ((instance->stop_mode == ME4600_AO_INF_STOP_MODE)
                && (circ_buffer_count <= ME4600_AO_FIFO_COUNT)) { //Change to hardware wraparound
                  PDEBUG
                      ("Changeing mode from software wraparound to hardware wraparound.\n");
                  //Copy all data
                  count =
                      ao_write_data(instance, circ_buffer_count,
                                instance->preloaded_count);
                  ctrl &= ~ME4600_AO_CTRL_MODE_MASK;
                  ctrl |= ME4600_AO_MODE_WRAPAROUND;
            }

            if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) {   //Reset position indicator.
                  instance->preloaded_count = 0;
            } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) {   //This should never happend!
                  PERROR_CRITICAL
                      ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n");
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
                  ME_SUBDEVICE_EXIT;
                  return ME_ERRNO_INTERNAL;
            }
      }

      //Set status to 'wait for start'
      instance->status = ao_status_stream_run_wait;

      status = inl(instance->status_reg);
      //Start state machine and interrupts
      ctrl &= ~(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
      if (instance->start_mode == ME4600_AO_EXT_TRIG) {     // External trigger.
            PINFO("External trigger.\n");
            ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
      }
      if (!(status & ME4600_AO_STATUS_BIT_HF)) {      //More than half!
            if ((ctrl & ME4600_AO_CTRL_MODE_MASK) == ME4600_AO_MODE_CONTINUOUS) {   //Enable IRQ only when hardware_continous is set and FIFO is more than half
                  ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
                  ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
            }
      }
      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base, ctrl);
      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      //Trigger output
      if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {     //Trigger outputs
            spin_lock(instance->preload_reg_lock);
            synch = inl(instance->preload_reg);
            //Add channel to start list
            outl(synch | (ME4600_AO_SYNC_HOLD << instance->ao_idx),
                 instance->preload_reg);
            PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->preload_reg - instance->reg_base,
                     synch | (ME4600_AO_SYNC_HOLD << instance->ao_idx));

            //Fire
            PINFO
                ("Fired all software synchronous outputs by software trigger.\n");
            outl(0x8000, instance->single_reg);
            PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->single_reg - instance->reg_base, 0x8000);

            //Restore save settings
            outl(synch, instance->preload_reg);
            PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->preload_reg - instance->reg_base, synch);
            spin_unlock(instance->preload_reg_lock);
      } else if (!instance->start_mode) { //Trigger outputs
/*
            //Remove channel from start list.   // <== Unnecessary. Removed.
            spin_lock(instance->preload_reg_lock);
                  synch = inl(instance->preload_reg);
                  outl(synch & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx), instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch & ~(ME4600_AO_SYNC_HOLD << instance->ao_idx));
*/
            //Fire
            PINFO("Software trigger.\n");
            outl(0x8000, instance->single_reg);
            PDEBUG_REG("single_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->single_reg - instance->reg_base, 0x8000);

/*
                  //Restore save settings.      // <== Unnecessary. Removed.
                  outl(synch, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base, instance->preload_reg - instance->reg_base, synch);
            spin_unlock(instance->preload_reg_lock);
*/
      }
      // Set control task's timeout
      ref = jiffies;
      instance->timeout.delay = delay;
      instance->timeout.start_time = ref;

      if (status & ME4600_AO_STATUS_BIT_HF) {   //Less than half but not empty!
            PINFO("Less than half.\n");
            if (instance->stop_data_count != 0) {
                  count = ME4600_AO_FIFO_COUNT / 2;
            } else {
                  count =
                      ((ME4600_AO_FIFO_COUNT / 2) <
                       instance->stop_data_count) ? ME4600_AO_FIFO_COUNT /
                      2 : instance->stop_data_count;
            }

            //Copy data
            count =
                ao_write_data(instance, count, instance->preloaded_count);

            if (count < 0) {  //This should never happend!
                  PERROR_CRITICAL("COPY FINISH WITH ERROR!\n");
                  ME_SUBDEVICE_EXIT;
                  return ME_ERRNO_INTERNAL;
            }

            if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
                  instance->circ_buf.tail += count;
                  instance->circ_buf.tail &= instance->circ_buf.mask;
            } else {    //Wraparound
                  instance->data_count += count;
                  instance->preloaded_count += count;

                  if (instance->preloaded_count == me_circ_buf_values(&instance->circ_buf)) {   //Reset position indicator.
                        instance->preloaded_count = 0;
                  } else if (instance->preloaded_count > me_circ_buf_values(&instance->circ_buf)) {   //This should never happend!
                        PERROR_CRITICAL
                            ("PRELOADED MORE VALUES THAN ARE IN BUFFER!\n");
                        ME_SUBDEVICE_EXIT;
                        return ME_ERRNO_INTERNAL;
                  }
            }

            status = inl(instance->status_reg);
            if (!(status & ME4600_AO_STATUS_BIT_HF)) {      //More than half!
                  spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
                  ctrl = inl(instance->ctrl_reg);
                  ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
                  ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(ctrl, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           ctrl);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            }
      }
      //Special case: Limited wraparound with less than HALF FIFO datas need work around to generate first interrupt.
      if ((instance->stop_mode != ME4600_AO_INF_STOP_MODE)
          && (instance->mode == ME4600_AO_SW_WRAP_MODE)
          && (circ_buffer_count <= (ME4600_AO_FIFO_COUNT / 2))) { //Put more data to FIFO
            PINFO("Limited wraparound with less than HALF FIFO datas.\n");
            if (instance->preloaded_count) {    //This should never happend!
                  PERROR_CRITICAL
                      ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n");
                  ME_SUBDEVICE_EXIT;
                  return ME_ERRNO_INTERNAL;
            }

            while (instance->stop_data_count > instance->data_count) {  //Maximum data not set jet.
                  //Copy to buffer
                  if (circ_buffer_count != ao_write_data(instance, circ_buffer_count, 0)) {     //This should never happend!
                        PERROR_CRITICAL
                            ("ERROR WHEN LOADING VALUES FOR WRAPAROUND!\n");
                        ME_SUBDEVICE_EXIT;
                        return ME_ERRNO_INTERNAL;
                  }
                  instance->data_count += circ_buffer_count;

                  if (!((status = inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_HF)) {      //FIFO is more than half. Enable IRQ and end copy.
                        spin_lock_irqsave(&instance->subdevice_lock,
                                      cpu_flags);
                        ctrl = inl(instance->ctrl_reg);
                        ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
                        ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                        outl(ctrl, instance->ctrl_reg);
                        PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                                 instance->reg_base,
                                 instance->ctrl_reg -
                                 instance->reg_base, ctrl);
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        break;
                  }
            }
      }
      // Schedule control task.
      instance->ao_control_task_flag = 1;
      queue_delayed_work(instance->me4600_workqueue,
                     &instance->ao_control_task, 1);

      if (start_mode == ME_START_MODE_BLOCKING) {     //Wait for start.
            //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
            wait_event_interruptible_timeout(instance->wait_queue,
                                     (instance->status !=
                                      ao_status_stream_run_wait),
                                     (delay) ? delay +
                                     1 : LONG_MAX);

            if ((instance->status != ao_status_stream_run)
                && (instance->status != ao_status_stream_end)) {
                  PDEBUG("Starting stream canceled. %d\n",
                         instance->status);
                  err = ME_ERRNO_CANCELLED;
            }

            if (signal_pending(current)) {
                  PERROR("Wait on start of state machine interrupted.\n");
                  instance->status = ao_status_none;
                  ao_stop_immediately(instance);
                  err = ME_ERRNO_SIGNAL;
            } else if ((delay) && ((jiffies - ref) >= delay)) {
                  if (instance->status != ao_status_stream_run) {
                        if (instance->status == ao_status_stream_end) {
                              PDEBUG("Timeout reached.\n");
                        } else {
                              if ((jiffies - ref) > delay) {
                                    PERROR
                                        ("Timeout reached. Not handled by control task!\n");
                              } else {
                                    PERROR
                                        ("Timeout reached. Signal come but status is strange: %d\n",
                                         instance->status);
                              }
                              ao_stop_immediately(instance);
                        }

                        instance->ao_control_task_flag = 0;
                        cancel_delayed_work(&instance->ao_control_task);
                        instance->status = ao_status_stream_end;
                        err = ME_ERRNO_TIMEOUT;
                  }
            }
      }

      ME_SUBDEVICE_EXIT;
      return err;
}

01838 static int me4600_ao_io_stream_status(me_subdevice_t *subdevice,
                              struct file *filep,
                              int wait,
                              int *status, int *values, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      if (flags) {
            PERROR("Invalid flag specified.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if ((wait != ME_WAIT_NONE) && (wait != ME_WAIT_IDLE)) {
            PERROR("Invalid wait argument specified.\n");
            *status = ME_STATUS_INVALID;
            return ME_ERRNO_INVALID_WAIT;
      }

      ME_SUBDEVICE_ENTER;

      switch (instance->status) {
      case ao_status_single_configured:
      case ao_status_single_end:
      case ao_status_stream_configured:
      case ao_status_stream_end:
      case ao_status_stream_fifo_error:
      case ao_status_stream_buffer_error:
      case ao_status_stream_error:
            *status = ME_STATUS_IDLE;
            break;

      case ao_status_single_run_wait:
      case ao_status_single_run:
      case ao_status_single_end_wait:
      case ao_status_stream_run_wait:
      case ao_status_stream_run:
      case ao_status_stream_end_wait:
            *status = ME_STATUS_BUSY;
            break;

      case ao_status_none:
      default:
            *status =
                (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ?
                ME_STATUS_BUSY : ME_STATUS_IDLE;
            break;
      }

      if ((wait == ME_WAIT_IDLE) && (*status == ME_STATUS_BUSY)) {
            //Only runing process will interrupt this call. Events are signaled when status change. Extra timeout add for safe reason.
            wait_event_interruptible_timeout(instance->wait_queue,
                                     ((instance->status !=
                                       ao_status_single_run_wait)
                                      && (instance->status !=
                                          ao_status_single_run)
                                      && (instance->status !=
                                          ao_status_single_end_wait)
                                      && (instance->status !=
                                          ao_status_stream_run_wait)
                                      && (instance->status !=
                                          ao_status_stream_run)
                                      && (instance->status !=
                                          ao_status_stream_end_wait)),
                                     LONG_MAX);

            if (instance->status != ao_status_stream_end) {
                  PDEBUG("Wait for IDLE canceled. %d\n",
                         instance->status);
                  err = ME_ERRNO_CANCELLED;
            }

            if (signal_pending(current)) {
                  PERROR("Wait for IDLE interrupted.\n");
                  instance->status = ao_status_none;
                  ao_stop_immediately(instance);
                  err = ME_ERRNO_SIGNAL;
            }

            *status = ME_STATUS_IDLE;
      }

      *values = me_circ_buf_space(&instance->circ_buf);

      ME_SUBDEVICE_EXIT;

      return err;
}

01936 static int me4600_ao_io_stream_stop(me_subdevice_t *subdevice,
                            struct file *filep,
                            int stop_mode, int flags)
{                       // Stop work and empty buffer and FIFO
      int err = ME_ERRNO_SUCCESS;
      me4600_ao_subdevice_t *instance;
      unsigned long cpu_flags;
      volatile uint32_t ctrl;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (flags & ~ME_IO_STREAM_STOP_PRESERVE_BUFFERS) {
            PERROR("Invalid flag specified.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if ((stop_mode != ME_STOP_MODE_IMMEDIATE)
          && (stop_mode != ME_STOP_MODE_LAST_VALUE)) {
            PERROR("Invalid stop mode specified.\n");
            return ME_ERRNO_INVALID_STOP_MODE;
      }

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      if (instance->status < ao_status_stream_configured) {
            //There is nothing to stop!
            PERROR("Subdevice not in streaming mode. %d\n",
                   instance->status);
            return ME_ERRNO_PREVIOUS_CONFIG;
      }

      ME_SUBDEVICE_ENTER;

      //Mark as stopping. => Software stop.
      instance->status = ao_status_stream_end_wait;

      if (stop_mode == ME_STOP_MODE_IMMEDIATE) {      //Stopped now!
            err = ao_stop_immediately(instance);
      } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) {
            ctrl = inl(instance->ctrl_reg) & ME4600_AO_CTRL_MODE_MASK;
            if (ctrl == ME4600_AO_MODE_WRAPAROUND) {  //Hardware wraparound => Hardware stop.
                  spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
                  ctrl = inl(instance->ctrl_reg);
                  ctrl |=
                      ME4600_AO_CTRL_BIT_STOP |
                      ME4600_AO_CTRL_BIT_RESET_IRQ;
                  ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(ctrl, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           ctrl);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            }
            //Only runing process will interrupt this call. Events are signaled when status change.
            wait_event_interruptible_timeout(instance->wait_queue,
                                     (instance->status !=
                                      ao_status_stream_end_wait),
                                     LONG_MAX);

            if (instance->status != ao_status_stream_end) {
                  PDEBUG("Stopping stream canceled.\n");
                  err = ME_ERRNO_CANCELLED;
            }

            if (signal_pending(current)) {
                  PERROR("Stopping stream interrupted.\n");
                  instance->status = ao_status_none;
                  ao_stop_immediately(instance);
                  err = ME_ERRNO_SIGNAL;
            }
      }

      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
      ctrl = inl(instance->ctrl_reg);
      ctrl |=
          ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
          ME4600_AO_CTRL_BIT_RESET_IRQ;
      ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
      if (!flags) {           //Reset FIFO
            ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO;
      }
      outl(ctrl, instance->ctrl_reg);
      PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->ctrl_reg - instance->reg_base, ctrl);
      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      if (!flags) {           //Reset software buffer
            instance->circ_buf.head = 0;
            instance->circ_buf.tail = 0;
            instance->preloaded_count = 0;
            instance->data_count = 0;
      }

      ME_SUBDEVICE_EXIT;

      return err;
}

02041 static int me4600_ao_io_stream_write(me_subdevice_t *subdevice,
                             struct file *filep,
                             int write_mode,
                             int *values, int *count, int flags)
{
      int err = ME_ERRNO_SUCCESS;
      me4600_ao_subdevice_t *instance;
      unsigned long cpu_flags = 0;
      uint32_t reg_copy;

      int copied_from_user = 0;
      int left_to_copy_from_user = *count;

      int copied_values;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      //Checking arguments
      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      if (flags) {
            PERROR("Invalid flag specified.\n");
            return ME_ERRNO_INVALID_FLAGS;
      }

      if (*count <= 0) {
            PERROR("Invalid count of values specified.\n");
            return ME_ERRNO_INVALID_VALUE_COUNT;
      }

      if (values == NULL) {
            PERROR("Invalid address of values specified.\n");
            return ME_ERRNO_INVALID_POINTER;
      }

      if ((instance->status == ao_status_none) || (instance->status == ao_status_single_configured)) {      //The device is in single mode.
            PERROR
                ("Subdevice must be preinitialize correctly for streaming.\n");
            return ME_ERRNO_PREVIOUS_CONFIG;
      }
/// @note If no 'pre-load' is used. stream_start() will move data to FIFO.
      switch (write_mode) {
      case ME_WRITE_MODE_PRELOAD:

            //Device must be stopped.
            if ((instance->status != ao_status_stream_configured)
                && (instance->status != ao_status_stream_end)) {
                  PERROR
                      ("Subdevice mustn't be runing when 'pre-load' mode is used.\n");
                  return ME_ERRNO_PREVIOUS_CONFIG;
            }
            break;
      case ME_WRITE_MODE_NONBLOCKING:
      case ME_WRITE_MODE_BLOCKING:
            /// @note In blocking mode: When device is not runing and there is not enought space call will blocked up!
            /// @note Some other thread must empty buffer by starting engine.
            break;

      default:
            PERROR("Invalid write mode specified.\n");
            return ME_ERRNO_INVALID_WRITE_MODE;
      }

      if (instance->mode & ME4600_AO_WRAP_MODE) {     //Wraparound mode. Device must be stopped.
            if ((instance->status != ao_status_stream_configured)
                && (instance->status != ao_status_stream_end)) {
                  PERROR
                      ("Subdevice mustn't be runing when 'pre-load' mode is used.\n");
                  return ME_ERRNO_INVALID_WRITE_MODE;
            }
      }

      if ((instance->mode == ME4600_AO_HW_WRAP_MODE) && (write_mode != ME_WRITE_MODE_PRELOAD)) {      // hardware wrap_around mode.
            //This is transparent for user.
            PDEBUG("Changing write_mode to ME_WRITE_MODE_PRELOAD.\n");
            write_mode = ME_WRITE_MODE_PRELOAD;
      }

      ME_SUBDEVICE_ENTER;

      if (write_mode == ME_WRITE_MODE_PRELOAD) {      //Init enviroment - preload
            spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
            reg_copy = inl(instance->ctrl_reg);
            //Check FIFO
            if (!(reg_copy & ME4600_AO_CTRL_BIT_ENABLE_FIFO)) {   //FIFO not active. Enable it.
                  reg_copy |= ME4600_AO_CTRL_BIT_ENABLE_FIFO;
                  outl(reg_copy, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           reg_copy);
                  instance->preloaded_count = 0;
            }
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
      }

      while (1) {
            //Copy to buffer. This step is common for all modes.
            copied_from_user =
                ao_get_data_from_user(instance, left_to_copy_from_user,
                                values + (*count -
                                        left_to_copy_from_user));
            left_to_copy_from_user -= copied_from_user;

            reg_copy = inl(instance->status_reg);
            if ((instance->status == ao_status_stream_run) && !(reg_copy & ME4600_AO_STATUS_BIT_FSM)) {     //BROKEN PIPE! The state machine is stoped but logical status show that should be working.
                  PERROR("Broken pipe in write.\n");
                  err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
                  break;
            }

            if ((instance->status == ao_status_stream_run) && (instance->mode == ME4600_AO_CONTINOUS) && (reg_copy & ME4600_AO_STATUS_BIT_HF)) {      //Continous mode runing and data are below half!

                  // Block interrupts.
                  spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
                  reg_copy = inl(instance->ctrl_reg);
                  //reg_copy &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  reg_copy |= ME4600_AO_CTRL_BIT_RESET_IRQ;
                  outl(reg_copy, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           reg_copy);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  //Fast copy
                  copied_values =
                      ao_write_data(instance, ME4600_AO_FIFO_COUNT / 2,
                                0);
                  if (copied_values > 0) {
                        instance->circ_buf.tail += copied_values;
                        instance->circ_buf.tail &=
                            instance->circ_buf.mask;
                        continue;
                  }
                  // Activate interrupts.
                  spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
                  reg_copy = inl(instance->ctrl_reg);
                  //reg_copy |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  reg_copy &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
                  outl(reg_copy, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           reg_copy);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  if (copied_values == 0) {     //This was checked and never should happend!
                        PERROR_CRITICAL("COPING FINISH WITH 0!\n");
                  }

                  if (copied_values < 0) {      //This was checked and never should happend!
                        PERROR_CRITICAL
                            ("COPING FINISH WITH AN ERROR!\n");
                        instance->status = ao_status_stream_fifo_error;
                        err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
                        break;
                  }
            }

            if (!left_to_copy_from_user) {      //All datas were copied.
                  break;
            } else {    //Not all datas were copied.
                  if (instance->mode & ME4600_AO_WRAP_MODE) {     //Error too much datas! Wraparound is limited in size!
                        PERROR
                            ("Too much data for wraparound mode!  Exceeded size of %ld.\n",
                             ME4600_AO_CIRC_BUF_COUNT - 1);
                        err = ME_ERRNO_RING_BUFFER_OVERFLOW;
                        break;
                  }

                  if (write_mode != ME_WRITE_MODE_BLOCKING) {     //Non blocking calls
                        break;
                  }

                  wait_event_interruptible(instance->wait_queue,
                                     me_circ_buf_space(&instance->
                                                   circ_buf));

                  if (signal_pending(current)) {
                        PERROR("Writing interrupted by signal.\n");
                        instance->status = ao_status_none;
                        ao_stop_immediately(instance);
                        err = ME_ERRNO_SIGNAL;
                        break;
                  }

                  if (instance->status == ao_status_none) { //Reset
                        PERROR("Writing interrupted by reset.\n");
                        err = ME_ERRNO_CANCELLED;
                        break;
                  }
            }
      }

      if (write_mode == ME_WRITE_MODE_PRELOAD) {      //Copy data to FIFO - preload
            copied_values =
                ao_write_data_pooling(instance, ME4600_AO_FIFO_COUNT,
                                instance->preloaded_count);
            instance->preloaded_count += copied_values;
            instance->data_count += copied_values;

            if ((instance->mode == ME4600_AO_HW_WRAP_MODE)
                && (me_circ_buf_values(&instance->circ_buf) >
                  ME4600_AO_FIFO_COUNT)) {
                  PERROR
                      ("Too much data for hardware wraparound mode! Exceeded size of %d.\n",
                       ME4600_AO_FIFO_COUNT);
                  err = ME_ERRNO_FIFO_BUFFER_OVERFLOW;
            }
      }

      *count = *count - left_to_copy_from_user;
      ME_SUBDEVICE_EXIT;

      return err;
}
02265 static irqreturn_t me4600_ao_isr(int irq, void *dev_id)
{
      me4600_ao_subdevice_t *instance = dev_id;
      uint32_t irq_status;
      uint32_t ctrl;
      uint32_t status;
      int count = 0;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (irq != instance->irq) {
            PERROR("Incorrect interrupt num: %d.\n", irq);
            return IRQ_NONE;
      }

      irq_status = inl(instance->irq_status_reg);
      if (!(irq_status & (ME4600_IRQ_STATUS_BIT_AO_HF << instance->ao_idx))) {
            PINFO("%ld Shared interrupt. %s(): ID=%d: status_reg=0x%04X\n",
                  jiffies, __func__, instance->ao_idx, irq_status);
            return IRQ_NONE;
      }

      if (!instance->circ_buf.buf) {
            instance->status = ao_status_stream_error;
            PERROR_CRITICAL("CIRCULAR BUFFER NOT EXISTS!\n");
            //Block interrupts. Stop machine.
            ctrl = inl(instance->ctrl_reg);
            ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
            ctrl |=
                ME4600_AO_CTRL_BIT_RESET_IRQ |
                ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP;
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);

            //Inform user
            wake_up_interruptible_all(&instance->wait_queue);
            return IRQ_HANDLED;
      }

      status = inl(instance->status_reg);
      if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     //Too late. Not working! END? BROKEN PIPE?
            PDEBUG("Interrupt come but ISM is not working!\n");
            //Block interrupts. Stop machine.
            ctrl = inl(instance->ctrl_reg);
            ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
            ctrl |=
                ME4600_AO_CTRL_BIT_RESET_IRQ | ME4600_AO_CTRL_BIT_STOP |
                ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);

            return IRQ_HANDLED;
      }
      //General procedure. Process more datas.

#ifdef MEDEBUG_DEBUG
      if (!me_circ_buf_values(&instance->circ_buf)) { //Buffer is empty!
            PDEBUG("Circular buffer empty!\n");
      }
#endif

      //Check FIFO
      if (status & ME4600_AO_STATUS_BIT_HF) {   //OK less than half

            //Block interrupts
            ctrl = inl(instance->ctrl_reg);
            ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
            ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);

            do {
                  //Calculate how many should be copied.
                  count =
                      (instance->stop_data_count) ? instance->
                      stop_data_count -
                      instance->data_count : ME4600_AO_FIFO_COUNT / 2;
                  if (ME4600_AO_FIFO_COUNT / 2 < count) {
                        count = ME4600_AO_FIFO_COUNT / 2;
                  }
                  //Copy data
                  if (instance->mode == ME4600_AO_CONTINOUS) {    //Continous
                        count = ao_write_data(instance, count, 0);
                        if (count > 0) {
                              instance->circ_buf.tail += count;
                              instance->circ_buf.tail &=
                                  instance->circ_buf.mask;
                              instance->data_count += count;

                              if ((instance->status == ao_status_stream_end_wait) && !me_circ_buf_values(&instance->circ_buf)) {    //Stoping. Whole buffer was copied.
                                    break;
                              }
                        }
                  } else if ((instance->mode == ME4600_AO_SW_WRAP_MODE) && ((ctrl & ME4600_AO_CTRL_MODE_MASK) == ME4600_AO_MODE_CONTINUOUS)) {  //Wraparound (software)
                        if (instance->status == ao_status_stream_end_wait) {  //We stoping => Copy to the end of the buffer.
                              count =
                                  ao_write_data(instance, count, 0);
                        } else {    //Copy in wraparound mode.
                              count =
                                  ao_write_data_wraparound(instance,
                                                     count,
                                                     instance->
                                                     preloaded_count);
                        }

                        if (count > 0) {
                              instance->data_count += count;
                              instance->preloaded_count += count;
                              instance->preloaded_count %=
                                  me_circ_buf_values(&instance->
                                                 circ_buf);

                              if ((instance->status == ao_status_stream_end_wait) && !instance->preloaded_count) {      //Stoping. Whole buffer was copied.
                                    break;
                              }
                        }
                  }

                  if ((count <= 0) || (instance->stop_data_count && (instance->stop_data_count <= instance->data_count))) {   //End of work.
                        break;
                  }
            }           //Repeat if still is under half fifo
            while ((status =
                  inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_HF);

            //Unblock interrupts
            ctrl = inl(instance->ctrl_reg);
            if (count >= 0) { //Copy was successful.
                  if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) {   //Finishing work. No more interrupts.
                        PDEBUG("Finishing work. Interrupt disabled.\n");
                        instance->status = ao_status_stream_end_wait;
                  } else if (count > 0) { //Normal work. Enable interrupt.
                        PDEBUG("Normal work. Enable interrupt.\n");
                        ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
                        ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  } else {    //Normal work but there are no more data in buffer. Interrupt active but blocked. stream_write() will unblock it.
                        PDEBUG
                            ("No data in software buffer. Interrupt blocked.\n");
                        ctrl |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  }
            } else {    //Error during copy.
                  instance->status = ao_status_stream_fifo_error;
            }

            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);
      } else {          //?? more than half
            PDEBUG
                ("Interrupt come but FIFO more than half full! Reset interrupt.\n");
            //Reset pending interrupt
            ctrl = inl(instance->ctrl_reg);
            ctrl |= ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);
            ctrl &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);
      }

      PINFO("ISR: Buffer count: %d.(T:%d H:%d)\n",
            me_circ_buf_values(&instance->circ_buf), instance->circ_buf.tail,
            instance->circ_buf.head);
      PINFO("ISR: Stop count: %d.\n", instance->stop_count);
      PINFO("ISR: Stop data count: %d.\n", instance->stop_data_count);
      PINFO("ISR: Data count: %d.\n", instance->data_count);

      //Inform user
      wake_up_interruptible_all(&instance->wait_queue);

      return IRQ_HANDLED;
}

02449 static void me4600_ao_destructor(struct me_subdevice *subdevice)
{
      me4600_ao_subdevice_t *instance;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      instance->ao_control_task_flag = 0;

      // Reset subdevice to asure clean exit.
      me4600_ao_io_reset_subdevice(subdevice, NULL,
                             ME_IO_RESET_SUBDEVICE_NO_FLAGS);

      // Remove any tasks from work queue. This is paranoic because it was done allready in reset().
      if (!cancel_delayed_work(&instance->ao_control_task)) {     //Wait 2 ticks to be sure that control task is removed from queue.
            set_current_state(TASK_INTERRUPTIBLE);
            schedule_timeout(2);
      }

      if (instance->fifo) {
            if (instance->irq) {
                  free_irq(instance->irq, instance);
                  instance->irq = 0;
            }

            if (instance->circ_buf.buf) {
                  free_pages((unsigned long)instance->circ_buf.buf,
                           ME4600_AO_CIRC_BUF_SIZE_ORDER);
            }
            instance->circ_buf.buf = NULL;
      }

      me_subdevice_deinit(&instance->base);
      kfree(instance);
}

me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
                                   spinlock_t *preload_reg_lock,
                                   uint32_t *preload_flags,
                                   int ao_idx,
                                   int fifo,
                                   int irq,
                                   struct workqueue_struct *me4600_wq)
{
      me4600_ao_subdevice_t *subdevice;
      int err;

      PDEBUG("executed. idx=%d\n", ao_idx);

      // Allocate memory for subdevice instance.
      subdevice = kmalloc(sizeof(me4600_ao_subdevice_t), GFP_KERNEL);

      if (!subdevice) {
            PERROR("Cannot get memory for subdevice instance.\n");
            return NULL;
      }

      memset(subdevice, 0, sizeof(me4600_ao_subdevice_t));

      // Initialize subdevice base class.
      err = me_subdevice_init(&subdevice->base);

      if (err) {
            PERROR("Cannot initialize subdevice base class instance.\n");
            kfree(subdevice);
            return NULL;
      }
      // Initialize spin locks.
      spin_lock_init(&subdevice->subdevice_lock);

      subdevice->preload_reg_lock = preload_reg_lock;
      subdevice->preload_flags = preload_flags;

      // Store analog output index.
      subdevice->ao_idx = ao_idx;

      // Store if analog output has fifo.
      subdevice->fifo = (ao_idx < fifo) ? 1 : 0;

      if (subdevice->fifo) {  // Allocate and initialize circular buffer.
            subdevice->circ_buf.mask = ME4600_AO_CIRC_BUF_COUNT - 1;

            subdevice->circ_buf.buf =
                (void *)__get_free_pages(GFP_KERNEL,
                                   ME4600_AO_CIRC_BUF_SIZE_ORDER);
            PDEBUG("circ_buf = %p size=%ld\n", subdevice->circ_buf.buf,
                   ME4600_AO_CIRC_BUF_SIZE);

            if (!subdevice->circ_buf.buf) {
                  PERROR
                      ("Cannot initialize subdevice base class instance.\n");
                  kfree(subdevice);
                  return NULL;
            }

            memset(subdevice->circ_buf.buf, 0, ME4600_AO_CIRC_BUF_SIZE);
      } else {          // No FIFO.
            subdevice->circ_buf.mask = 0;
            subdevice->circ_buf.buf = NULL;
      }

      subdevice->circ_buf.head = 0;
      subdevice->circ_buf.tail = 0;

      subdevice->status = ao_status_none;
      subdevice->ao_control_task_flag = 0;
      subdevice->timeout.delay = 0;
      subdevice->timeout.start_time = jiffies;

      // Initialize wait queue.
      init_waitqueue_head(&subdevice->wait_queue);

      // Initialize single value to 0V.
      subdevice->single_value = 0x8000;
      subdevice->single_value_in_fifo = 0x8000;

      // Register interrupt service routine.
      if (subdevice->fifo) {
            subdevice->irq = irq;
            if (request_irq(subdevice->irq, me4600_ao_isr,
                        IRQF_DISABLED | IRQF_SHARED,
                        ME4600_NAME, subdevice)) {
                  PERROR("Cannot get interrupt line.\n");
                  PDEBUG("free circ_buf = %p size=%d",
                         subdevice->circ_buf.buf,
                         PAGE_SHIFT << ME4600_AO_CIRC_BUF_SIZE_ORDER);
                  free_pages((unsigned long)subdevice->circ_buf.buf,
                           ME4600_AO_CIRC_BUF_SIZE_ORDER);
                  me_subdevice_deinit((me_subdevice_t *) subdevice);
                  kfree(subdevice);
                  return NULL;
            }
            PINFO("Registered irq=%d.\n", subdevice->irq);
      } else {
            subdevice->irq = 0;
      }

      // Initialize registers.
      subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
      subdevice->preload_reg = reg_base + ME4600_AO_SYNC_REG;
      if (ao_idx == 0) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_00_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_00_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_00_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_00_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_00_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bitpattern = 0;
      } else if (ao_idx == 1) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_01_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_01_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_01_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_01_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_01_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bitpattern = 0;
      } else if (ao_idx == 2) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_02_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_02_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_02_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_02_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_02_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bitpattern = 0;
      } else if (ao_idx == 3) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_03_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_03_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_03_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_03_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_03_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bitpattern = 1;
      } else {
            PERROR_CRITICAL("WRONG SUBDEVICE idx=%d!", ao_idx);
            me_subdevice_deinit((me_subdevice_t *) subdevice);
            if (subdevice->fifo) {
                  free_pages((unsigned long)subdevice->circ_buf.buf,
                           ME4600_AO_CIRC_BUF_SIZE_ORDER);
            }
            subdevice->circ_buf.buf = NULL;
            kfree(subdevice);
            return NULL;
      }

      // Override base class methods.
      subdevice->base.me_subdevice_destructor = me4600_ao_destructor;
      subdevice->base.me_subdevice_io_reset_subdevice =
          me4600_ao_io_reset_subdevice;
      subdevice->base.me_subdevice_io_single_config =
          me4600_ao_io_single_config;
      subdevice->base.me_subdevice_io_single_read = me4600_ao_io_single_read;
      subdevice->base.me_subdevice_io_single_write =
          me4600_ao_io_single_write;
      subdevice->base.me_subdevice_io_stream_config =
          me4600_ao_io_stream_config;
      subdevice->base.me_subdevice_io_stream_new_values =
          me4600_ao_io_stream_new_values;
      subdevice->base.me_subdevice_io_stream_write =
          me4600_ao_io_stream_write;
      subdevice->base.me_subdevice_io_stream_start =
          me4600_ao_io_stream_start;
      subdevice->base.me_subdevice_io_stream_status =
          me4600_ao_io_stream_status;
      subdevice->base.me_subdevice_io_stream_stop = me4600_ao_io_stream_stop;
      subdevice->base.me_subdevice_query_number_channels =
          me4600_ao_query_number_channels;
      subdevice->base.me_subdevice_query_subdevice_type =
          me4600_ao_query_subdevice_type;
      subdevice->base.me_subdevice_query_subdevice_caps =
          me4600_ao_query_subdevice_caps;
      subdevice->base.me_subdevice_query_subdevice_caps_args =
          me4600_ao_query_subdevice_caps_args;
      subdevice->base.me_subdevice_query_range_by_min_max =
          me4600_ao_query_range_by_min_max;
      subdevice->base.me_subdevice_query_number_ranges =
          me4600_ao_query_number_ranges;
      subdevice->base.me_subdevice_query_range_info =
          me4600_ao_query_range_info;
      subdevice->base.me_subdevice_query_timer = me4600_ao_query_timer;

      // Prepare work queue
      subdevice->me4600_workqueue = me4600_wq;

/* workqueue API changed in kernel 2.6.20 */
      INIT_DELAYED_WORK(&subdevice->ao_control_task,
                    me4600_ao_work_control_task);

      if (subdevice->fifo) {  // Set speed for single operations.
            outl(ME4600_AO_MIN_CHAN_TICKS - 1, subdevice->timer_reg);
            subdevice->hardware_stop_delay = HZ / 10; //100ms
      }

      return subdevice;
}

/** @brief Stop presentation. Preserve FIFOs.
*
* @param instance The subdevice instance (pointer).
*/
02689 inline int ao_stop_immediately(me4600_ao_subdevice_t *instance)
{
      unsigned long cpu_flags;
      uint32_t ctrl;
      int timeout;
      int i;

      timeout =
          (instance->hardware_stop_delay >
           (HZ / 10)) ? instance->hardware_stop_delay : HZ / 10;
      for (i = 0; i <= timeout; i++) {
            spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
            // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched!
            ctrl = inl(instance->ctrl_reg);
            ctrl |=
                ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
                | ME4600_AO_CTRL_BIT_RESET_IRQ;
            ctrl &=
                ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                  ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {    // Exit.
                  break;
            }
            //Still working!
            set_current_state(TASK_INTERRUPTIBLE);
            schedule_timeout(1);
      }

      if (i > timeout) {
            PERROR_CRITICAL("FSM IS BUSY!\n");
            return ME_ERRNO_INTERNAL;
      }
      return ME_ERRNO_SUCCESS;
}

/** @brief Copy data from circular buffer to fifo (fast) in wraparound.
* @note This is time critical function. Checking is done at begining and end only.
* @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly.
*
* @param instance The subdevice instance (pointer).
* @param count Maximum number of copied data.
* @param start_pos Position of the firs value in buffer.
*
* @return On success: Number of copied data.
* @return On error/success: 0.      No datas were copied => no data in buffer.
* @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW.
*/
02742 inline int ao_write_data_wraparound(me4600_ao_subdevice_t *instance, int count,
                            int start_pos)
{                       /// @note This is time critical function!
      uint32_t status;
      uint32_t value;
      int pos =
          (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
      int local_count = count;
      int i = 1;

      if (count <= 0) { //Wrong count!
            return 0;
      }

      while (i < local_count) {
            //Get value from buffer
            value = *(instance->circ_buf.buf + pos);
            //Prepare it
            if (instance->ao_idx & 0x1) {
                  value <<= 16;
            }
            //Put value to FIFO
            outl(value, instance->fifo_reg);
            //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);

            pos++;
            pos &= instance->circ_buf.mask;
            if (pos == instance->circ_buf.head) {
                  pos = instance->circ_buf.tail;
            }
            i++;
      }

      status = inl(instance->status_reg);
      if (!(status & ME4600_AO_STATUS_BIT_FF)) {      //FIFO is full before all datas were copied!
            PERROR("FIFO was full before all datas were copied! idx=%d\n",
                   instance->ao_idx);
            return -ME_ERRNO_FIFO_BUFFER_OVERFLOW;
      } else {          //Add last value
            value = *(instance->circ_buf.buf + pos);
            if (instance->ao_idx & 0x1) {
                  value <<= 16;
            }
            //Put value to FIFO
            outl(value, instance->fifo_reg);
            //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
      }

      PINFO("WRAPAROUND LOADED %d values. idx=%d\n", local_count,
            instance->ao_idx);
      return local_count;
}

/** @brief Copy data from software buffer to fifo (fast).
* @note This is time critical function. Checking is done at begining and end only.
* @note The is not reasonable way to check how many walues was in FIFO at begining. The count must be managed externaly.
*
* @param instance The subdevice instance (pointer).
* @param count Maximum number of copied data.
* @param start_pos Position of the firs value in buffer.
*
* @return On success: Number of copied data.
* @return On error/success: 0.      No datas were copied => no data in buffer.
* @return On error: -ME_ERRNO_FIFO_BUFFER_OVERFLOW.
*/
02807 inline int ao_write_data(me4600_ao_subdevice_t *instance, int count,
                   int start_pos)
{                       /// @note This is time critical function!
      uint32_t status;
      uint32_t value;
      int pos =
          (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
      int local_count = count;
      int max_count;
      int i = 1;

      if (count <= 0) { //Wrong count!
            return 0;
      }

      max_count = me_circ_buf_values(&instance->circ_buf) - start_pos;
      if (max_count <= 0) {   //No data to copy!
            return 0;
      }

      if (max_count < count) {
            local_count = max_count;
      }

      while (i < local_count) {
            //Get value from buffer
            value = *(instance->circ_buf.buf + pos);
            //Prepare it
            if (instance->ao_idx & 0x1) {
                  value <<= 16;
            }
            //Put value to FIFO
            outl(value, instance->fifo_reg);
            //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);

            pos++;
            pos &= instance->circ_buf.mask;
            i++;
      }

      status = inl(instance->status_reg);
      if (!(status & ME4600_AO_STATUS_BIT_FF)) {      //FIFO is full before all datas were copied!
            PERROR("FIFO was full before all datas were copied! idx=%d\n",
                   instance->ao_idx);
            return -ME_ERRNO_FIFO_BUFFER_OVERFLOW;
      } else {          //Add last value
            value = *(instance->circ_buf.buf + pos);
            if (instance->ao_idx & 0x1) {
                  value <<= 16;
            }
            //Put value to FIFO
            outl(value, instance->fifo_reg);
            //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);
      }

      PINFO("FAST LOADED %d values. idx=%d\n", local_count, instance->ao_idx);
      return local_count;
}

/** @brief Copy data from software buffer to fifo (slow).
* @note This is slow function that copy all data from buffer to FIFO with full control.
*
* @param instance The subdevice instance (pointer).
* @param count Maximum number of copied data.
* @param start_pos Position of the firs value in buffer.
*
* @return On success: Number of copied values.
* @return On error/success: 0.      FIFO was full at begining.
* @return On error: -ME_ERRNO_RING_BUFFER_UNDEFFLOW.
*/
02877 inline int ao_write_data_pooling(me4600_ao_subdevice_t *instance, int count,
                         int start_pos)
{                       /// @note This is slow function!
      uint32_t status;
      uint32_t value;
      int pos =
          (instance->circ_buf.tail + start_pos) & instance->circ_buf.mask;
      int local_count = count;
      int i;
      int max_count;

      if (count <= 0) { //Wrong count!
            PERROR("SLOW LOADED: Wrong count! idx=%d\n", instance->ao_idx);
            return 0;
      }

      max_count = me_circ_buf_values(&instance->circ_buf) - start_pos;
      if (max_count <= 0) {   //No data to copy!
            PERROR("SLOW LOADED: No data to copy! idx=%d\n",
                   instance->ao_idx);
            return 0;
      }

      if (max_count < count) {
            local_count = max_count;
      }

      for (i = 0; i < local_count; i++) {
            status = inl(instance->status_reg);
            if (!(status & ME4600_AO_STATUS_BIT_FF)) {      //FIFO is full!
                  return i;
            }
            //Get value from buffer
            value = *(instance->circ_buf.buf + pos);
            //Prepare it
            if (instance->ao_idx & 0x1) {
                  value <<= 16;
            }
            //Put value to FIFO
            outl(value, instance->fifo_reg);
            //PDEBUG_REG("idx=%d fifo_reg outl(0x%lX+0x%lX)=0x%x\n", instance->ao_idx, instance->reg_base, instance->fifo_reg - instance->reg_base, value);

            pos++;
            pos &= instance->circ_buf.mask;
      }

      PINFO("SLOW LOADED %d values. idx=%d\n", local_count, instance->ao_idx);
      return local_count;
}

/** @brief Copy data from user space to circular buffer.
* @param instance The subdevice instance (pointer).
* @param count Number of datas in user space.
* @param user_values Buffer's pointer.
*
* @return On success: Number of copied values.
* @return On error: -ME_ERRNO_INTERNAL.
*/
02935 inline int ao_get_data_from_user(me4600_ao_subdevice_t *instance, int count,
                         int *user_values)
{
      int i, err;
      int empty_space;
      int copied;
      int value;

      empty_space = me_circ_buf_space(&instance->circ_buf);
      //We have only this space free.
      copied = (count < empty_space) ? count : empty_space;
      for (i = 0; i < copied; i++) {      //Copy from user to buffer
            if ((err = get_user(value, (int *)(user_values + i)))) {
                  PERROR
                      ("BUFFER LOADED: get_user(0x%p) return an error: %d. idx=%d\n",
                       user_values + i, err, instance->ao_idx);
                  return -ME_ERRNO_INTERNAL;
            }
            /// @note The analog output in me4600 series has size of 16 bits.
            *(instance->circ_buf.buf + instance->circ_buf.head) =
                (uint16_t) value;
            instance->circ_buf.head++;
            instance->circ_buf.head &= instance->circ_buf.mask;
      }

      PINFO("BUFFER LOADED %d values. idx=%d\n", copied, instance->ao_idx);
      return copied;
}

/** @brief Checking actual hardware and logical state.
* @param instance The subdevice instance (pointer).
*/
02967 static void me4600_ao_work_control_task(struct work_struct *work)
{
      me4600_ao_subdevice_t *instance;
      unsigned long cpu_flags = 0;
      uint32_t status;
      uint32_t ctrl;
      uint32_t synch;
      int reschedule = 0;
      int signaling = 0;

      instance =
          container_of((void *)work, me4600_ao_subdevice_t, ao_control_task);
      PINFO("<%s: %ld> executed. idx=%d\n", __func__, jiffies,
            instance->ao_idx);

      status = inl(instance->status_reg);
      PDEBUG_REG("status_reg inl(0x%lX+0x%lX)=0x%x\n", instance->reg_base,
               instance->status_reg - instance->reg_base, status);

      switch (instance->status) {   // Checking actual mode.

            // Not configured for work.
      case ao_status_none:
            break;

            //This are stable modes. No need to do anything. (?)
      case ao_status_single_configured:
      case ao_status_stream_configured:
      case ao_status_stream_fifo_error:
      case ao_status_stream_buffer_error:
      case ao_status_stream_error:
            PERROR("Shouldn't be running!.\n");
            break;

      case ao_status_stream_end:
            if (!instance->fifo) {
                  PERROR_CRITICAL
                      ("Streaming on single device! This feature is not implemented in this version!\n");
                  instance->status = ao_status_stream_error;
                  // Signal the end.
                  signaling = 1;
                  break;
            }
      case ao_status_single_end:
            if (status & ME4600_AO_STATUS_BIT_FSM) {  // State machine is working but the status is set to end. Force stop.

                  // Wait for stop.
                  reschedule = 1;
            }

            spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
            // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched!
            ctrl = inl(instance->ctrl_reg);
            ctrl |=
                ME4600_AO_CTRL_BIT_IMMEDIATE_STOP | ME4600_AO_CTRL_BIT_STOP
                | ME4600_AO_CTRL_BIT_RESET_IRQ;
            ctrl &=
                ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                  ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
            ctrl &=
                ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
                  ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
            outl(ctrl, instance->ctrl_reg);
            PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                     instance->reg_base,
                     instance->ctrl_reg - instance->reg_base, ctrl);
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
            break;

            // Single modes
      case ao_status_single_run_wait:
      case ao_status_single_run:
      case ao_status_single_end_wait:

            if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     // State machine is not working.
                  if (((instance->fifo)
                       && (!(status & ME4600_AO_STATUS_BIT_EF)))
                      || (!(instance->fifo))) { // Single is in end state.
                        PDEBUG("Single call has been complited.\n");

                        // Set correct value for single_read();
                        instance->single_value =
                            instance->single_value_in_fifo;

                        // Set status as 'ao_status_single_end'
                        instance->status = ao_status_single_end;

                        // Signal the end.
                        signaling = 1;
                        // Wait for stop ISM.
                        reschedule = 1;

                        break;
                  }
            }
            // Check timeout.
            if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {   // Timeout
                  PDEBUG("Timeout reached.\n");
                  // Stop all actions. No conditions! Block interrupts and trigger. Leave FIFO untouched!
                  spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
                  ctrl = inl(instance->ctrl_reg);
                  ctrl |=
                      ME4600_AO_CTRL_BIT_STOP |
                      ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
                      ME4600_AO_CTRL_BIT_RESET_IRQ;
                  ctrl &=
                      ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                        ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
                  /// Fix for timeout error.
                  ctrl &=
                      ~(ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
                        ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH);
                  if (instance->fifo) {   //Disabling FIFO
                        ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO;
                  }
                  outl(ctrl, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           ctrl);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  spin_lock(instance->preload_reg_lock);
                  //Remove from synchronous start. Block triggering from this output.
                  synch = inl(instance->preload_reg);
                  synch &=
                      ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) <<
                        instance->ao_idx);
                  if (!(instance->fifo)) {      // No FIFO - set to single safe mode
                        synch |=
                            ME4600_AO_SYNC_HOLD << instance->ao_idx;
                  }
                  outl(synch, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           synch);
                  spin_unlock(instance->preload_reg_lock);

                  if (!(instance->fifo)) {      // No FIFO
                        // Restore old settings.
                        PDEBUG("Write old value back to register.\n");
                        outl(instance->single_value,
                             instance->single_reg);
                        PDEBUG_REG
                            ("single_reg outl(0x%lX+0x%lX)=0x%x\n",
                             instance->reg_base,
                             instance->single_reg - instance->reg_base,
                             instance->single_value);
                  }
                  // Set correct value for single_read();
                  instance->single_value_in_fifo = instance->single_value;

                  instance->status = ao_status_single_end;

                  // Signal the end.
                  signaling = 1;
            }
            // Wait for stop.
            reschedule = 1;
            break;

            // Stream modes
      case ao_status_stream_run_wait:
            if (!instance->fifo) {
                  PERROR_CRITICAL
                      ("Streaming on single device! This feature is not implemented in this version!\n");
                  instance->status = ao_status_stream_error;
                  // Signal the end.
                  signaling = 1;
                  break;
            }

            if (status & ME4600_AO_STATUS_BIT_FSM) {  // State machine is working. Waiting for start finish.
                  instance->status = ao_status_stream_run;

                  // Signal end of this step
                  signaling = 1;
            } else {    // State machine is not working.
                  if (!(status & ME4600_AO_STATUS_BIT_EF)) {      // FIFO is empty. Procedure has started and finish already!
                        instance->status = ao_status_stream_end;

                        // Signal the end.
                        signaling = 1;
                        // Wait for stop.
                        reschedule = 1;
                        break;
                  }
            }

            // Check timeout.
            if ((instance->timeout.delay) && ((jiffies - instance->timeout.start_time) >= instance->timeout.delay)) {   // Timeout
                  PDEBUG("Timeout reached.\n");
                  // Stop all actions. No conditions! Block interrupts. Leave FIFO untouched!
                  spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
                  ctrl = inl(instance->ctrl_reg);
                  ctrl |=
                      ME4600_AO_CTRL_BIT_STOP |
                      ME4600_AO_CTRL_BIT_IMMEDIATE_STOP |
                      ME4600_AO_CTRL_BIT_RESET_IRQ;
                  ctrl &=
                      ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                        ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
                  outl(ctrl, instance->ctrl_reg);
                  PDEBUG_REG("ctrl_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->ctrl_reg - instance->reg_base,
                           ctrl);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
                  spin_lock(instance->preload_reg_lock);
                  //Remove from synchronous start. Block triggering from this output.
                  synch = inl(instance->preload_reg);
                  synch &=
                      ~((ME4600_AO_SYNC_HOLD | ME4600_AO_SYNC_EXT_TRIG) <<
                        instance->ao_idx);
                  outl(synch, instance->preload_reg);
                  PDEBUG_REG("preload_reg outl(0x%lX+0x%lX)=0x%x\n",
                           instance->reg_base,
                           instance->preload_reg - instance->reg_base,
                           synch);
                  spin_unlock(instance->preload_reg_lock);

                  instance->status = ao_status_stream_end;

                  // Signal the end.
                  signaling = 1;
            }
            // Wait for stop.
            reschedule = 1;
            break;

      case ao_status_stream_run:
            if (!instance->fifo) {
                  PERROR_CRITICAL
                      ("Streaming on single device! This feature is not implemented in this version!\n");
                  instance->status = ao_status_stream_error;
                  // Signal the end.
                  signaling = 1;
                  break;
            }

            if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     // State machine is not working. This is an error.
                  // BROKEN PIPE!
                  if (!(status & ME4600_AO_STATUS_BIT_EF)) {      // FIFO is empty.
                        if (me_circ_buf_values(&instance->circ_buf)) {  // Software buffer is not empty.
                              if (instance->stop_data_count && (instance->stop_data_count <= instance->data_count)) {   //Finishing work. Requed data shown.
                                    PDEBUG
                                        ("ISM stoped. No data in FIFO. Buffer is not empty.\n");
                                    instance->status =
                                        ao_status_stream_end;
                              } else {
                                    PERROR
                                        ("Output stream has been broken. ISM stoped. No data in FIFO. Buffer is not empty.\n");
                                    instance->status =
                                        ao_status_stream_buffer_error;
                              }
                        } else {    // Software buffer is empty.
                              PDEBUG
                                  ("ISM stoped. No data in FIFO. Buffer is empty.\n");
                              instance->status = ao_status_stream_end;
                        }
                  } else {    // There are still datas in FIFO.
                        if (me_circ_buf_values(&instance->circ_buf)) {  // Software buffer is not empty.
                              PERROR
                                  ("Output stream has been broken. ISM stoped but some data in FIFO and buffer.\n");
                        } else {    // Software buffer is empty.
                              PERROR
                                  ("Output stream has been broken. ISM stoped but some data in FIFO. Buffer is empty.\n");
                        }
                        instance->status = ao_status_stream_fifo_error;

                  }

                  // Signal the failure.
                  signaling = 1;
                  break;
            }
            // Wait for stop.
            reschedule = 1;
            break;

      case ao_status_stream_end_wait:
            if (!instance->fifo) {
                  PERROR_CRITICAL
                      ("Streaming on single device! This feature is not implemented in this version!\n");
                  instance->status = ao_status_stream_error;
                  // Signal the end.
                  signaling = 1;
                  break;
            }

            if (!(status & ME4600_AO_STATUS_BIT_FSM)) {     // State machine is not working. Waiting for stop finish.
                  instance->status = ao_status_stream_end;
                  signaling = 1;
            }
            // State machine is working.
            reschedule = 1;
            break;

      default:
            PERROR_CRITICAL("Status is in wrong state (%d)!\n",
                        instance->status);
            instance->status = ao_status_stream_error;
            // Signal the end.
            signaling = 1;
            break;

      }

      if (signaling) {  //Signal it.
            wake_up_interruptible_all(&instance->wait_queue);
      }

      if (instance->ao_control_task_flag && reschedule) {   // Reschedule task
            queue_delayed_work(instance->me4600_workqueue,
                           &instance->ao_control_task, 1);
      } else {
            PINFO("<%s> Ending control task.\n", __func__);
      }

}
#else
/// @note SPECIAL BUILD FOR BOSCH
/// @author Guenter Gebhardt
static int me4600_ao_io_reset_subdevice(me_subdevice_t *subdevice,
                              struct file *filep, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      uint32_t tmp;
      unsigned long status;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      ME_SUBDEVICE_ENTER spin_lock_irqsave(&instance->subdevice_lock, status);
      spin_lock(instance->preload_reg_lock);
      tmp = inl(instance->preload_reg);
      tmp &= ~(0x10001 << instance->ao_idx);
      outl(tmp, instance->preload_reg);
      *instance->preload_flags &= ~(0x1 << instance->ao_idx);
      spin_unlock(instance->preload_reg_lock);

      tmp = inl(instance->ctrl_reg);
      tmp |= ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
      outl(tmp, instance->ctrl_reg);

      while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ;

      outl(ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP,
           instance->ctrl_reg);

      outl(0x8000, instance->single_reg);

      instance->single_value = 0x8000;
      instance->circ_buf.head = 0;
      instance->circ_buf.tail = 0;

      spin_unlock_irqrestore(&instance->subdevice_lock, status);

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_single_config(me_subdevice_t *subdevice,
                              struct file *filep,
                              int channel,
                              int single_config,
                              int ref,
                              int trig_chan,
                              int trig_type, int trig_edge, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      uint32_t tmp;
      unsigned long cpu_flags;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      ME_SUBDEVICE_ENTER
          spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);

      if (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) {
            PERROR("Subdevice is busy.\n");
            err = ME_ERRNO_SUBDEVICE_BUSY;
            goto ERROR;
      }

      if (channel == 0) {
            if (single_config == 0) {
                  if (ref == ME_REF_AO_GROUND) {
                        if (trig_chan == ME_TRIG_CHAN_DEFAULT) {
                              if (trig_type == ME_TRIG_TYPE_SW) {
                                    tmp = inl(instance->ctrl_reg);
                                    tmp |=
                                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                    outl(tmp, instance->ctrl_reg);
                                    tmp =
                                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                    outl(tmp, instance->ctrl_reg);

                                    spin_lock(instance->
                                            preload_reg_lock);
                                    tmp =
                                        inl(instance->preload_reg);
                                    tmp &=
                                        ~(0x10001 << instance->
                                          ao_idx);
                                    outl(tmp,
                                         instance->preload_reg);
                                    *instance->preload_flags &=
                                        ~(0x1 << instance->ao_idx);
                                    spin_unlock(instance->
                                              preload_reg_lock);
                              } else if (trig_type ==
                                       ME_TRIG_TYPE_EXT_DIGITAL) {
                                    if (trig_edge ==
                                        ME_TRIG_EDGE_RISING) {
                                          tmp =
                                              inl(instance->
                                                ctrl_reg);
                                          tmp |=
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                          tmp =
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
                                              |
                                              ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                    } else if (trig_edge ==
                                             ME_TRIG_EDGE_FALLING)
                                    {
                                          tmp =
                                              inl(instance->
                                                ctrl_reg);
                                          tmp |=
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                          tmp =
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
                                              |
                                              ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG
                                              |
                                              ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                    } else if (trig_edge ==
                                             ME_TRIG_EDGE_ANY) {
                                          tmp =
                                              inl(instance->
                                                ctrl_reg);
                                          tmp |=
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                          tmp =
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
                                              |
                                              ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG
                                              |
                                              ME4600_AO_CTRL_BIT_EX_TRIG_EDGE
                                              |
                                              ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                    } else {
                                          PERROR
                                              ("Invalid trigger edge.\n");
                                          err =
                                              ME_ERRNO_INVALID_TRIG_EDGE;
                                          goto ERROR;
                                    }

                                    spin_lock(instance->
                                            preload_reg_lock);

                                    tmp =
                                        inl(instance->preload_reg);
                                    tmp &=
                                        ~(0x10001 << instance->
                                          ao_idx);
                                    tmp |= 0x1 << instance->ao_idx;
                                    outl(tmp,
                                         instance->preload_reg);
                                    *instance->preload_flags &=
                                        ~(0x1 << instance->ao_idx);
                                    spin_unlock(instance->
                                              preload_reg_lock);
                              } else {
                                    PERROR
                                        ("Invalid trigger type.\n");
                                    err =
                                        ME_ERRNO_INVALID_TRIG_TYPE;
                                    goto ERROR;
                              }
                        } else if (trig_chan ==
                                 ME_TRIG_CHAN_SYNCHRONOUS) {
                              if (trig_type == ME_TRIG_TYPE_SW) {
                                    tmp = inl(instance->ctrl_reg);
                                    tmp |=
                                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                    outl(tmp, instance->ctrl_reg);
                                    tmp =
                                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                    outl(tmp, instance->ctrl_reg);

                                    spin_lock(instance->
                                            preload_reg_lock);
                                    tmp =
                                        inl(instance->preload_reg);
                                    tmp &=
                                        ~(0x10001 << instance->
                                          ao_idx);
                                    tmp |= 0x1 << instance->ao_idx;
                                    outl(tmp,
                                         instance->preload_reg);
                                    *instance->preload_flags |=
                                        0x1 << instance->ao_idx;
                                    spin_unlock(instance->
                                              preload_reg_lock);
                              } else if (trig_type ==
                                       ME_TRIG_TYPE_EXT_DIGITAL) {
                                    if (trig_edge ==
                                        ME_TRIG_EDGE_RISING) {
                                          tmp =
                                              inl(instance->
                                                ctrl_reg);
                                          tmp |=
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                          tmp =
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                    } else if (trig_edge ==
                                             ME_TRIG_EDGE_FALLING)
                                    {
                                          tmp =
                                              inl(instance->
                                                ctrl_reg);
                                          tmp |=
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                          tmp =
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
                                              |
                                              ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                    } else if (trig_edge ==
                                             ME_TRIG_EDGE_ANY) {
                                          tmp =
                                              inl(instance->
                                                ctrl_reg);
                                          tmp |=
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                          tmp =
                                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP
                                              |
                                              ME4600_AO_CTRL_BIT_EX_TRIG_EDGE
                                              |
                                              ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;
                                          outl(tmp,
                                               instance->
                                               ctrl_reg);
                                    } else {
                                          PERROR
                                              ("Invalid trigger edge.\n");
                                          err =
                                              ME_ERRNO_INVALID_TRIG_EDGE;
                                          goto ERROR;
                                    }

                                    spin_lock(instance->
                                            preload_reg_lock);

                                    tmp =
                                        inl(instance->preload_reg);
                                    tmp |=
                                        0x10001 << instance->ao_idx;
                                    outl(tmp,
                                         instance->preload_reg);
                                    *instance->preload_flags &=
                                        ~(0x1 << instance->ao_idx);
                                    spin_unlock(instance->
                                              preload_reg_lock);
                              } else {
                                    PERROR
                                        ("Invalid trigger type.\n");
                                    err =
                                        ME_ERRNO_INVALID_TRIG_TYPE;
                                    goto ERROR;
                              }
                        } else {
                              PERROR
                                  ("Invalid trigger channel specified.\n");
                              err = ME_ERRNO_INVALID_REF;
                              goto ERROR;
                        }
                  } else {
                        PERROR("Invalid analog reference specified.\n");
                        err = ME_ERRNO_INVALID_REF;
                        goto ERROR;
                  }
            } else {
                  PERROR("Invalid single config specified.\n");
                  err = ME_ERRNO_INVALID_SINGLE_CONFIG;
                  goto ERROR;
            }
      } else {
            PERROR("Invalid channel number specified.\n");
            err = ME_ERRNO_INVALID_CHANNEL;
            goto ERROR;
      }

ERROR:

      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_single_read(me_subdevice_t *subdevice,
                            struct file *filep,
                            int channel,
                            int *value, int time_out, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      unsigned long tmp;
      unsigned long cpu_flags;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      if (channel != 0) {
            PERROR("Invalid channel number specified.\n");
            return ME_ERRNO_INVALID_CHANNEL;
      }

      ME_SUBDEVICE_ENTER
          spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
      tmp = inl(instance->ctrl_reg);

      if (tmp & 0x3) {
            PERROR("Not in single mode.\n");
            err = ME_ERRNO_PREVIOUS_CONFIG;
      } else {
            *value = instance->single_value;
      }

      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_single_write(me_subdevice_t *subdevice,
                             struct file *filep,
                             int channel,
                             int value, int time_out, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      unsigned long mask = 0;
      unsigned long tmp;
      unsigned long cpu_flags;
      int i;
      wait_queue_head_t queue;
      unsigned long j;
      unsigned long delay = 0;

      PDEBUG("executed.\n");

      init_waitqueue_head(&queue);

      instance = (me4600_ao_subdevice_t *) subdevice;

      if (channel != 0) {
            PERROR("Invalid channel number specified.\n");
            return ME_ERRNO_INVALID_CHANNEL;
      }

      if (time_out < 0) {
            PERROR("Invalid timeout specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      if (time_out) {
            delay = (time_out * HZ) / 1000;

            if (delay == 0)
                  delay = 1;
      }

      ME_SUBDEVICE_ENTER
          spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);

      tmp = inl(instance->ctrl_reg);

      if (tmp & 0x3) {
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
            PERROR("Not in single mode.\n");
            err = ME_ERRNO_PREVIOUS_CONFIG;
            goto ERROR;
      }

      if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) {
            outl(value, instance->single_reg);
            instance->single_value = value;
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

            if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
                  j = jiffies;

                  while (inl(instance->status_reg) &
                         ME4600_AO_STATUS_BIT_FSM) {
                        interruptible_sleep_on_timeout(&queue, 1);

                        if (signal_pending(current)) {
                              PERROR
                                  ("Wait on external trigger interrupted by signal.\n");
                              err = ME_ERRNO_SIGNAL;
                              goto ERROR;
                        }

                        if (delay && ((jiffies - j) > delay)) {
                              PERROR("Timeout reached.\n");
                              err = ME_ERRNO_TIMEOUT;
                              goto ERROR;
                        }
                  }
            }
      } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx))
               == (0x10001 << instance->ao_idx)) {
            if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {
                  tmp |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
                  outl(tmp, instance->ctrl_reg);
                  outl(value, instance->single_reg);
                  instance->single_value = value;
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  if (!(flags & ME_IO_SINGLE_TYPE_WRITE_NONBLOCKING)) {
                        j = jiffies;

                        while (inl(instance->status_reg) &
                               ME4600_AO_STATUS_BIT_FSM) {
                              interruptible_sleep_on_timeout(&queue,
                                                       1);

                              if (signal_pending(current)) {
                                    PERROR
                                        ("Wait on external trigger interrupted by signal.\n");
                                    err = ME_ERRNO_SIGNAL;
                                    goto ERROR;
                              }

                              if (delay && ((jiffies - j) > delay)) {
                                    PERROR("Timeout reached.\n");
                                    err = ME_ERRNO_TIMEOUT;
                                    goto ERROR;
                              }
                        }
                  }
            } else {
                  outl(value, instance->single_reg);
                  instance->single_value = value;
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            }
      } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx))
               == (0x1 << instance->ao_idx)) {
            outl(value, instance->single_reg);
            instance->single_value = value;

            PDEBUG("Synchronous SW, flags = 0x%X.\n", flags);

            if (flags & ME_IO_SINGLE_TYPE_TRIG_SYNCHRONOUS) {
                  PDEBUG("Trigger synchronous SW.\n");
                  spin_lock(instance->preload_reg_lock);
                  tmp = inl(instance->preload_reg);

                  for (i = 0; i < ME4600_AO_MAX_SUBDEVICES; i++) {
                        if ((*instance->preload_flags & (0x1 << i))) {
                              if ((tmp & (0x10001 << i)) ==
                                  (0x1 << i)) {
                                    mask |= 0x1 << i;
                              }
                        }
                  }

                  tmp &= ~(mask);

                  outl(tmp, instance->preload_reg);
                  spin_unlock(instance->preload_reg_lock);
            }

            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
      } else {
            outl(value, instance->single_reg);
            instance->single_value = value;
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
      }

ERROR:

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_stream_config(me_subdevice_t *subdevice,
                              struct file *filep,
                              meIOStreamConfig_t *config_list,
                              int count,
                              meIOStreamTrigger_t *trigger,
                              int fifo_irq_threshold, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      unsigned long ctrl;
      unsigned long tmp;
      unsigned long cpu_flags;
      uint64_t conv_ticks;
      unsigned int conv_start_ticks_low = trigger->iConvStartTicksLow;
      unsigned int conv_start_ticks_high = trigger->iConvStartTicksHigh;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      conv_ticks =
          (uint64_t) conv_start_ticks_low +
          ((uint64_t) conv_start_ticks_high << 32);

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      ME_SUBDEVICE_ENTER
          spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);

      if ((inl(instance->status_reg)) & ME4600_AO_STATUS_BIT_FSM) {
            PERROR("Subdevice is busy.\n");
            err = ME_ERRNO_SUBDEVICE_BUSY;
            goto ERROR;
      }

      ctrl = inl(instance->ctrl_reg);
      ctrl |= ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
      outl(ctrl, instance->ctrl_reg);
      ctrl = ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
      outl(ctrl, instance->ctrl_reg);

      if (count != 1) {
            PERROR("Invalid stream configuration list count specified.\n");
            err = ME_ERRNO_INVALID_CONFIG_LIST_COUNT;
            goto ERROR;
      }

      if (config_list[0].iChannel != 0) {
            PERROR("Invalid channel number specified.\n");
            err = ME_ERRNO_INVALID_CHANNEL;
            goto ERROR;
      }

      if (config_list[0].iStreamConfig != 0) {
            PERROR("Invalid stream config specified.\n");
            err = ME_ERRNO_INVALID_STREAM_CONFIG;
            goto ERROR;
      }

      if (config_list[0].iRef != ME_REF_AO_GROUND) {
            PERROR("Invalid analog reference.\n");
            err = ME_ERRNO_INVALID_REF;
            goto ERROR;
      }

      if ((trigger->iAcqStartTicksLow != 0)
          || (trigger->iAcqStartTicksHigh != 0)) {
            PERROR
                ("Invalid acquisition start trigger argument specified.\n");
            err = ME_ERRNO_INVALID_ACQ_START_ARG;
            goto ERROR;
      }

      switch (trigger->iAcqStartTrigType) {

      case ME_TRIG_TYPE_SW:
            break;

      case ME_TRIG_TYPE_EXT_DIGITAL:
            ctrl |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;

            switch (trigger->iAcqStartTrigEdge) {

            case ME_TRIG_EDGE_RISING:
                  break;

            case ME_TRIG_EDGE_FALLING:
                  ctrl |= ME4600_AO_CTRL_BIT_EX_TRIG_EDGE;

                  break;

            case ME_TRIG_EDGE_ANY:
                  ctrl |=
                      ME4600_AO_CTRL_BIT_EX_TRIG_EDGE |
                      ME4600_AO_CTRL_BIT_EX_TRIG_EDGE_BOTH;

                  break;

            default:
                  PERROR
                      ("Invalid acquisition start trigger edge specified.\n");

                  err = ME_ERRNO_INVALID_ACQ_START_TRIG_EDGE;

                  goto ERROR;

                  break;
            }

            break;

      default:
            PERROR("Invalid acquisition start trigger type specified.\n");

            err = ME_ERRNO_INVALID_ACQ_START_TRIG_TYPE;

            goto ERROR;

            break;
      }

      switch (trigger->iScanStartTrigType) {

      case ME_TRIG_TYPE_FOLLOW:
            break;

      default:
            PERROR("Invalid scan start trigger type specified.\n");

            err = ME_ERRNO_INVALID_SCAN_START_TRIG_TYPE;

            goto ERROR;

            break;
      }

      switch (trigger->iConvStartTrigType) {

      case ME_TRIG_TYPE_TIMER:
            if ((conv_ticks < ME4600_AO_MIN_CHAN_TICKS)
                || (conv_ticks > ME4600_AO_MAX_CHAN_TICKS)) {
                  PERROR
                      ("Invalid conv start trigger argument specified.\n");
                  err = ME_ERRNO_INVALID_CONV_START_ARG;
                  goto ERROR;
            }

            break;

      default:
            PERROR("Invalid conv start trigger type specified.\n");

            err = ME_ERRNO_INVALID_CONV_START_TRIG_TYPE;

            goto ERROR;

            break;
      }

      /* Preset to hardware wraparound mode */
      instance->flags &= ~(ME4600_AO_FLAGS_SW_WRAP_MODE_MASK);

      switch (trigger->iScanStopTrigType) {

      case ME_TRIG_TYPE_NONE:
            if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
                  /* Set flags to indicate usage of software mode. */
                  instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_INF;
                  instance->wrap_count = 0;
                  instance->wrap_remaining = 0;
            }

            break;

      case ME_TRIG_TYPE_COUNT:
            if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
                  if (trigger->iScanStopCount <= 0) {
                        PERROR("Invalid scan stop count specified.\n");
                        err = ME_ERRNO_INVALID_SCAN_STOP_ARG;
                        goto ERROR;
                  }

                  /* Set flags to indicate usage of software mode. */
                  instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_FIN;
                  instance->wrap_count = trigger->iScanStopCount;
                  instance->wrap_remaining = trigger->iScanStopCount;
            } else {
                  PERROR("Invalid scan stop trigger type specified.\n");
                  err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
                  goto ERROR;
            }

            break;

      default:
            PERROR("Invalid scan stop trigger type specified.\n");

            err = ME_ERRNO_INVALID_SCAN_STOP_TRIG_TYPE;

            goto ERROR;

            break;
      }

      switch (trigger->iAcqStopTrigType) {

      case ME_TRIG_TYPE_NONE:
            break;

      case ME_TRIG_TYPE_COUNT:
            if (trigger->iScanStopTrigType != ME_TRIG_TYPE_NONE) {
                  PERROR("Invalid acq stop trigger type specified.\n");
                  err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
                  goto ERROR;
            }

            if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
                  if (trigger->iAcqStopCount <= 0) {
                        PERROR("Invalid acq stop count specified.\n");
                        err = ME_ERRNO_INVALID_ACQ_STOP_ARG;
                        goto ERROR;
                  }

                  /* Set flags to indicate usage of software mode. */
                  instance->flags |= ME4600_AO_FLAGS_SW_WRAP_MODE_FIN;
                  instance->wrap_count = trigger->iAcqStopCount;
                  instance->wrap_remaining = trigger->iAcqStopCount;
            } else {
                  PERROR("Invalid acp stop trigger type specified.\n");
                  err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
                  goto ERROR;
            }

            break;

      default:
            PERROR("Invalid acq stop trigger type specified.\n");
            err = ME_ERRNO_INVALID_ACQ_STOP_TRIG_TYPE;
            goto ERROR;
            break;
      }

      switch (trigger->iAcqStartTrigChan) {

      case ME_TRIG_CHAN_DEFAULT:
            spin_lock(instance->preload_reg_lock);
            tmp = inl(instance->preload_reg);
            tmp &= ~(0x10001 << instance->ao_idx);
            outl(tmp, instance->preload_reg);
            spin_unlock(instance->preload_reg_lock);

            break;

      case ME_TRIG_CHAN_SYNCHRONOUS:
            if (trigger->iAcqStartTrigType == ME_TRIG_TYPE_SW) {
                  spin_lock(instance->preload_reg_lock);
                  tmp = inl(instance->preload_reg);
                  tmp &= ~(0x10001 << instance->ao_idx);
                  outl(tmp, instance->preload_reg);
                  tmp |= 0x1 << instance->ao_idx;
                  outl(tmp, instance->preload_reg);
                  spin_unlock(instance->preload_reg_lock);
            } else {
                  ctrl &= ~(ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG);
                  spin_lock(instance->preload_reg_lock);
                  tmp = inl(instance->preload_reg);
                  tmp &= ~(0x10001 << instance->ao_idx);
                  outl(tmp, instance->preload_reg);
                  tmp |= 0x10000 << instance->ao_idx;
                  outl(tmp, instance->preload_reg);
                  spin_unlock(instance->preload_reg_lock);
            }

            break;

      default:
            PERROR("Invalid acq start trigger channel specified.\n");
            err = ME_ERRNO_INVALID_ACQ_START_TRIG_CHAN;
            goto ERROR;

            break;
      }

      outl(conv_ticks - 2, instance->timer_reg);

      if (flags & ME_IO_STREAM_CONFIG_BIT_PATTERN) {
            if (instance->ao_idx == 3) {
                  ctrl |= ME4600_AO_CTRL_BIT_ENABLE_DO;
            } else {
                  err = ME_ERRNO_INVALID_FLAGS;
                  goto ERROR;
            }
      } else {
            if (instance->ao_idx == 3) {
                  ctrl &= ~ME4600_AO_CTRL_BIT_ENABLE_DO;
            }
      }

      /* Set hardware mode. */
      if (flags & ME_IO_STREAM_CONFIG_WRAPAROUND) {
            ctrl |= ME4600_AO_CTRL_BIT_MODE_0;
      } else {
            ctrl |= ME4600_AO_CTRL_BIT_MODE_1;
      }

      PDEBUG("Preload word = 0x%X.\n", inl(instance->preload_reg));

      PDEBUG("Ctrl word = 0x%lX.\n", ctrl);
      outl(ctrl, instance->ctrl_reg);     // Write the control word

ERROR:

      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_stream_new_values(me_subdevice_t *subdevice,
                                struct file *filep,
                                int time_out, int *count, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      long t = 0;
      long j;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      if (time_out < 0) {
            PERROR("Invalid time_out specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      if (time_out) {
            t = (time_out * HZ) / 1000;

            if (t == 0)
                  t = 1;
      }

      *count = 0;

      ME_SUBDEVICE_ENTER;

      if (t) {
            j = jiffies;
            wait_event_interruptible_timeout(instance->wait_queue,
                                     ((me_circ_buf_space
                                       (&instance->circ_buf))
                                      || !(inl(instance->status_reg)
                                           &
                                           ME4600_AO_STATUS_BIT_FSM)),
                                     t);

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
                  PERROR("AO subdevice is not running.\n");
                  err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
            } else if (signal_pending(current)) {
                  PERROR("Wait on values interrupted from signal.\n");
                  err = ME_ERRNO_SIGNAL;
            } else if ((jiffies - j) >= t) {
                  PERROR("Wait on values timed out.\n");
                  err = ME_ERRNO_TIMEOUT;
            } else {
                  *count = me_circ_buf_space(&instance->circ_buf);
            }
      } else {
            wait_event_interruptible(instance->wait_queue,
                               ((me_circ_buf_space
                                 (&instance->circ_buf))
                                || !(inl(instance->status_reg) &
                                     ME4600_AO_STATUS_BIT_FSM)));

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
                  PERROR("AO subdevice is not running.\n");
                  err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
            } else if (signal_pending(current)) {
                  PERROR("Wait on values interrupted from signal.\n");
                  err = ME_ERRNO_SIGNAL;
            } else {
                  *count = me_circ_buf_space(&instance->circ_buf);
            }
      }

      ME_SUBDEVICE_EXIT;

      return err;
}

static void stop_immediately(me4600_ao_subdevice_t *instance)
{
      unsigned long cpu_flags;
      uint32_t tmp;

      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
      tmp = inl(instance->ctrl_reg);
      tmp |= ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
      outl(tmp, instance->ctrl_reg);

      while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ;

      spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
}

static int me4600_ao_io_stream_start(me_subdevice_t *subdevice,
                             struct file *filep,
                             int start_mode, int time_out, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      unsigned long cpu_flags = 0;
      unsigned long ref;
      unsigned long tmp;
      unsigned long delay = 0;
      wait_queue_head_t queue;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      init_waitqueue_head(&queue);

      if (time_out < 0) {
            PERROR("Invalid timeout specified.\n");
            return ME_ERRNO_INVALID_TIMEOUT;
      }

      if (time_out) {
            delay = (time_out * HZ) / 1000;

            if (delay == 0)
                  delay = 1;
      }

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      ME_SUBDEVICE_ENTER
          spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);

      tmp = inl(instance->ctrl_reg);

      switch (tmp & (ME4600_AO_CTRL_MASK_MODE)) {

      case 0:           // Single mode
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
            PERROR("Subdevice is configured in single mode.\n");
            err = ME_ERRNO_PREVIOUS_CONFIG;
            goto ERROR;

      case 1:           // Wraparound mode
            if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) {  // Normal wraparound with external trigger

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                        ME4600_AO_CTRL_BIT_STOP |
                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);

                  outl(tmp, instance->ctrl_reg);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  if (start_mode == ME_START_MODE_BLOCKING) {
                        init_waitqueue_head(&queue);

                        if (delay) {
                              ref = jiffies;

                              while (!
                                     (inl(instance->status_reg) &
                                    ME4600_AO_STATUS_BIT_FSM)) {
                                    interruptible_sleep_on_timeout
                                        (&queue, 1);

                                    if (signal_pending(current)) {
                                          PERROR
                                              ("Wait on start of state machine interrupted.\n");
                                          stop_immediately
                                              (instance);
                                          err = ME_ERRNO_SIGNAL;
                                          goto ERROR;
                                    }

                                    if (((jiffies - ref) >= delay)) {
                                          PERROR
                                              ("Timeout reached.\n");
                                          stop_immediately
                                              (instance);
                                          err = ME_ERRNO_TIMEOUT;
                                          goto ERROR;
                                    }
                              }
                        } else {
                              while (!
                                     (inl(instance->status_reg) &
                                    ME4600_AO_STATUS_BIT_FSM)) {
                                    interruptible_sleep_on_timeout
                                        (&queue, 1);

                                    if (signal_pending(current)) {
                                          PERROR
                                              ("Wait on start of state machine interrupted.\n");
                                          stop_immediately
                                              (instance);
                                          err = ME_ERRNO_SIGNAL;
                                          goto ERROR;
                                    }
                              }
                        }
                  } else if (start_mode == ME_START_MODE_NONBLOCKING) {
                  } else {
                        PERROR("Invalid start mode specified.\n");
                        err = ME_ERRNO_INVALID_START_MODE;
                        goto ERROR;
                  }
            } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x10000 << instance->ao_idx)) { // Synchronous with external trigger

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
                        tmp |= ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG;
                        tmp &=
                            ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                              ME4600_AO_CTRL_BIT_STOP |
                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
                        outl(tmp, instance->ctrl_reg);
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);

                        if (start_mode == ME_START_MODE_BLOCKING) {
                              init_waitqueue_head(&queue);

                              if (delay) {
                                    ref = jiffies;

                                    while (!
                                           (inl
                                          (instance->
                                           status_reg) &
                                          ME4600_AO_STATUS_BIT_FSM))
                                    {
                                          interruptible_sleep_on_timeout
                                              (&queue, 1);

                                          if (signal_pending
                                              (current)) {
                                                PERROR
                                                    ("Wait on start of state machine interrupted.\n");
                                                stop_immediately
                                                    (instance);
                                                err =
                                                    ME_ERRNO_SIGNAL;
                                                goto ERROR;
                                          }

                                          if (((jiffies - ref) >=
                                               delay)) {
                                                PERROR
                                                    ("Timeout reached.\n");
                                                stop_immediately
                                                    (instance);
                                                err =
                                                    ME_ERRNO_TIMEOUT;
                                                goto ERROR;
                                          }
                                    }
                              } else {
                                    while (!
                                           (inl
                                          (instance->
                                           status_reg) &
                                          ME4600_AO_STATUS_BIT_FSM))
                                    {
                                          interruptible_sleep_on_timeout
                                              (&queue, 1);

                                          if (signal_pending
                                              (current)) {
                                                PERROR
                                                    ("Wait on start of state machine interrupted.\n");
                                                stop_immediately
                                                    (instance);
                                                err =
                                                    ME_ERRNO_SIGNAL;
                                                goto ERROR;
                                          }
                                    }
                              }
                        } else if (start_mode ==
                                 ME_START_MODE_NONBLOCKING) {
                        } else {
                              PERROR
                                  ("Invalid start mode specified.\n");
                              err = ME_ERRNO_INVALID_START_MODE;
                              goto ERROR;
                        }
                  } else {
                        tmp &=
                            ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                              ME4600_AO_CTRL_BIT_STOP |
                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
                        outl(tmp, instance->ctrl_reg);
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                  }
            } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x1 << instance->ao_idx)) {     // Synchronous wraparound with sw trigger

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                        ME4600_AO_CTRL_BIT_STOP |
                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);

                  outl(tmp, instance->ctrl_reg);

                  if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
                        outl(0x8000, instance->single_reg);
                        instance->single_value = 0x8000;
                  }

                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            } else {    // Software start

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_ENABLE_IRQ |
                        ME4600_AO_CTRL_BIT_STOP |
                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);

                  outl(tmp, instance->ctrl_reg);

                  outl(0x8000, instance->single_reg);
                  instance->single_value = 0x8000;

                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            }

            break;

      case 2:           // Continuous mode
            if (tmp & ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG) {  // Externally triggered

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_STOP |
                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
                  tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(tmp, instance->ctrl_reg);
                  instance->wrap_remaining = instance->wrap_count;
                  instance->circ_buf.tail = 0;
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  if (start_mode == ME_START_MODE_BLOCKING) {
                        init_waitqueue_head(&queue);

                        if (delay) {
                              ref = jiffies;

                              while (!
                                     (inl(instance->status_reg) &
                                    ME4600_AO_STATUS_BIT_FSM)) {
                                    interruptible_sleep_on_timeout
                                        (&queue, 1);

                                    if (signal_pending(current)) {
                                          PERROR
                                              ("Wait on start of state machine interrupted.\n");
                                          stop_immediately
                                              (instance);
                                          err = ME_ERRNO_SIGNAL;
                                          goto ERROR;
                                    }

                                    if (((jiffies - ref) >= delay)) {
                                          PERROR
                                              ("Timeout reached.\n");
                                          stop_immediately
                                              (instance);
                                          err = ME_ERRNO_TIMEOUT;
                                          goto ERROR;
                                    }
                              }
                        } else {
                              while (!
                                     (inl(instance->status_reg) &
                                    ME4600_AO_STATUS_BIT_FSM)) {
                                    interruptible_sleep_on_timeout
                                        (&queue, 1);

                                    if (signal_pending(current)) {
                                          PERROR
                                              ("Wait on start of state machine interrupted.\n");
                                          stop_immediately
                                              (instance);
                                          err = ME_ERRNO_SIGNAL;
                                          goto ERROR;
                                    }
                              }
                        }
                  } else if (start_mode == ME_START_MODE_NONBLOCKING) {
                        /* Do nothing */
                  } else {
                        PERROR("Invalid start mode specified.\n");
                        stop_immediately(instance);
                        err = ME_ERRNO_INVALID_START_MODE;
                        goto ERROR;
                  }
            } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x10000 << instance->ao_idx)) { // Synchronous with external trigger

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
                        tmp |=
                            ME4600_AO_CTRL_BIT_ENABLE_EX_TRIG |
                            ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                        tmp &=
                            ~(ME4600_AO_CTRL_BIT_STOP |
                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
                        outl(tmp, instance->ctrl_reg);
                        instance->wrap_remaining = instance->wrap_count;
                        instance->circ_buf.tail = 0;

                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);

                        if (start_mode == ME_START_MODE_BLOCKING) {
                              init_waitqueue_head(&queue);

                              if (delay) {
                                    ref = jiffies;

                                    while (!
                                           (inl
                                          (instance->
                                           status_reg) &
                                          ME4600_AO_STATUS_BIT_FSM))
                                    {
                                          interruptible_sleep_on_timeout
                                              (&queue, 1);

                                          if (signal_pending
                                              (current)) {
                                                PERROR
                                                    ("Wait on start of state machine interrupted.\n");
                                                stop_immediately
                                                    (instance);
                                                err =
                                                    ME_ERRNO_SIGNAL;
                                                goto ERROR;
                                          }

                                          if (((jiffies - ref) >=
                                               delay)) {
                                                PERROR
                                                    ("Timeout reached.\n");
                                                stop_immediately
                                                    (instance);
                                                err =
                                                    ME_ERRNO_TIMEOUT;
                                                goto ERROR;
                                          }
                                    }
                              } else {
                                    while (!
                                           (inl
                                          (instance->
                                           status_reg) &
                                          ME4600_AO_STATUS_BIT_FSM))
                                    {
                                          interruptible_sleep_on_timeout
                                              (&queue, 1);

                                          if (signal_pending
                                              (current)) {
                                                PERROR
                                                    ("Wait on start of state machine interrupted.\n");
                                                stop_immediately
                                                    (instance);
                                                err =
                                                    ME_ERRNO_SIGNAL;
                                                goto ERROR;
                                          }
                                    }
                              }
                        } else if (start_mode ==
                                 ME_START_MODE_NONBLOCKING) {
                        } else {
                              PERROR
                                  ("Invalid start mode specified.\n");
                              stop_immediately(instance);
                              err = ME_ERRNO_INVALID_START_MODE;
                              goto ERROR;
                        }
                  } else {
                        tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                        tmp &=
                            ~(ME4600_AO_CTRL_BIT_STOP |
                              ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
                        outl(tmp, instance->ctrl_reg);
                        instance->wrap_remaining = instance->wrap_count;
                        instance->circ_buf.tail = 0;
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                  }
            } else if ((inl(instance->preload_reg) & (0x10001 << instance->ao_idx)) == (0x1 << instance->ao_idx)) {     // Synchronous wraparound with sw trigger

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_STOP |
                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);
                  tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  instance->wrap_remaining = instance->wrap_count;
                  instance->circ_buf.tail = 0;
                  PDEBUG("CTRL Reg = 0x%X.\n", inl(instance->ctrl_reg));
                  outl(tmp, instance->ctrl_reg);

                  if (flags & ME_IO_STREAM_START_TYPE_TRIG_SYNCHRONOUS) {
                        outl(0x8000, instance->single_reg);
                        instance->single_value = 0x8000;
                  }

                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            } else {    // Software start

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR("Conversion is already running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_STOP |
                        ME4600_AO_CTRL_BIT_IMMEDIATE_STOP);

                  tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(tmp, instance->ctrl_reg);
                  outl(0x8000, instance->single_reg);
                  instance->single_value = 0x8000;
                  instance->wrap_remaining = instance->wrap_count;
                  instance->circ_buf.tail = 0;
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
            }

            break;

      default:
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
            PERROR("Invalid mode configured.\n");
            err = ME_ERRNO_INTERNAL;
            goto ERROR;
      }

ERROR:

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_stream_status(me_subdevice_t *subdevice,
                              struct file *filep,
                              int wait,
                              int *status, int *values, int flags)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;
      wait_queue_head_t queue;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      init_waitqueue_head(&queue);

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      ME_SUBDEVICE_ENTER;

      if (wait == ME_WAIT_NONE) {
            *status =
                (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ?
                ME_STATUS_BUSY : ME_STATUS_IDLE;
            *values = me_circ_buf_space(&instance->circ_buf);
      } else if (wait == ME_WAIT_IDLE) {
            while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) {
                  interruptible_sleep_on_timeout(&queue, 1);

                  if (instance->flags & ME4600_AO_FLAGS_BROKEN_PIPE) {
                        PERROR("Output stream was interrupted.\n");
                        *status = ME_STATUS_ERROR;
                        err = ME_ERRNO_SUCCESS;
                        goto ERROR;
                  }

                  if (signal_pending(current)) {
                        PERROR
                            ("Wait on state machine interrupted by signal.\n");
                        *status = ME_STATUS_INVALID;
                        err = ME_ERRNO_SIGNAL;
                        goto ERROR;
                  }
            }

            *status = ME_STATUS_IDLE;

            *values = me_circ_buf_space(&instance->circ_buf);
      } else {
            PERROR("Invalid wait argument specified.\n");
            *status = ME_STATUS_INVALID;
            err = ME_ERRNO_INVALID_WAIT;
            goto ERROR;
      }

ERROR:

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_stream_stop(me_subdevice_t *subdevice,
                            struct file *filep,
                            int stop_mode, int flags)
{
      int err = ME_ERRNO_SUCCESS;
      me4600_ao_subdevice_t *instance;
      unsigned long cpu_flags;
      unsigned long tmp;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      ME_SUBDEVICE_ENTER;

      if (stop_mode == ME_STOP_MODE_IMMEDIATE) {
            spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
            tmp = inl(instance->ctrl_reg);
            tmp |=
                ME4600_AO_CTRL_BIT_STOP | ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
            outl(tmp, instance->ctrl_reg);

            while (inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM) ;

            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
      } else if (stop_mode == ME_STOP_MODE_LAST_VALUE) {
            spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);
            tmp = inl(instance->ctrl_reg);
            tmp |= ME4600_AO_CTRL_BIT_STOP;
            outl(tmp, instance->ctrl_reg);
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
      } else {
            PERROR("Invalid stop mode specified.\n");
            err = ME_ERRNO_INVALID_STOP_MODE;
            goto ERROR;
      }

ERROR:

      ME_SUBDEVICE_EXIT;

      return err;
}

static int me4600_ao_io_stream_write(me_subdevice_t *subdevice,
                             struct file *filep,
                             int write_mode,
                             int *values, int *count, int flags)
{
      int err = ME_ERRNO_SUCCESS;
      me4600_ao_subdevice_t *instance;
      unsigned long tmp;
      int i;
      int value;
      int cnt = *count;
      int c;
      int k;
      int ret = 0;
      unsigned long cpu_flags = 0;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      if (!instance->fifo) {
            PERROR("Not a streaming ao.\n");
            return ME_ERRNO_NOT_SUPPORTED;
      }

      ME_SUBDEVICE_ENTER;

      if (*count <= 0) {
            PERROR("Invalid count of values specified.\n");
            err = ME_ERRNO_INVALID_VALUE_COUNT;
            goto ERROR;
      }

      spin_lock_irqsave(&instance->subdevice_lock, cpu_flags);

      tmp = inl(instance->ctrl_reg);

      switch (tmp & 0x3) {

      case 1:           // Wraparound mode
            if (instance->bosch_fw) {     // Bosch firmware
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  if (cnt != 7) {
                        PERROR
                            ("Invalid count of values specified. 7 expected.\n");
                        err = ME_ERRNO_INVALID_VALUE_COUNT;
                        goto ERROR;
                  }

                  for (i = 0; i < 7; i++) {
                        if (get_user(value, values)) {
                              PERROR
                                  ("Can't copy value from user space.\n");
                              err = ME_ERRNO_INTERNAL;
                              goto ERROR;
                        }

                        if (i == 0) {
                              /* Maximum voltage */
                              value <<= 16;
                              value |=
                                  inl(instance->reg_base +
                                    0xD4) & 0xFFFF;
                              outl(value, instance->reg_base + 0xD4);
                        } else if (i == 1) {
                              /* Minimum voltage */
                              value &= 0xFFFF;
                              value |=
                                  inl(instance->reg_base +
                                    0xD4) & 0xFFFF0000;
                              outl(value, instance->reg_base + 0xD4);
                        } else if (i == 2) {
                              /* Delta up */
                              value <<= 16;
                              value |=
                                  inl(instance->reg_base +
                                    0xD8) & 0xFFFF;
                              outl(value, instance->reg_base + 0xD8);
                        } else if (i == 3) {
                              /* Delta down */
                              value &= 0xFFFF;
                              value |=
                                  inl(instance->reg_base +
                                    0xD8) & 0xFFFF0000;
                              outl(value, instance->reg_base + 0xD8);
                        } else if (i == 4) {
                              /* Start value */
                              outl(value, instance->reg_base + 0xDC);
                        } else if (i == 5) {
                              /* Invert */
                              if (value) {
                                    value = inl(instance->ctrl_reg);
                                    value |= 0x100;
                                    outl(value, instance->ctrl_reg);
                              } else {
                                    value = inl(instance->ctrl_reg);
                                    value &= ~0x100;
                                    outl(value, instance->ctrl_reg);
                              }
                        } else if (i == 6) {
                              /* Timer for positive ramp */
                              outl(value, instance->reg_base + 0xE0);
                        }

                        values++;
                  }
            } else {    // Normal firmware
                  PDEBUG("Write for wraparound mode.\n");

                  if (inl(instance->status_reg) &
                      ME4600_AO_STATUS_BIT_FSM) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR
                            ("There is already a conversion running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp |= ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                  tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_FIFO;
                  outl(tmp, instance->ctrl_reg);
                  tmp |= ME4600_AO_CTRL_BIT_ENABLE_FIFO;

                  if ((*count > ME4600_AO_FIFO_COUNT) ||
                      ((instance->
                        flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
                       ME4600_AO_FLAGS_SW_WRAP_MODE_FIN)) {
                        tmp &=
                            ~(ME4600_AO_CTRL_BIT_MODE_0 |
                              ME4600_AO_CTRL_BIT_MODE_1);
                        tmp |= ME4600_AO_CTRL_BIT_MODE_1;
                  }

                  outl(tmp, instance->ctrl_reg);
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  if ((*count <= ME4600_AO_FIFO_COUNT) &&
                      ((instance->
                        flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
                       ME4600_AO_FLAGS_SW_WRAP_MODE_INF)) {
                        for (i = 0; i < *count; i++) {
                              if (get_user(value, values + i)) {
                                    PERROR
                                        ("Cannot copy value from user space.\n");
                                    err = ME_ERRNO_INTERNAL;
                                    goto ERROR;
                              }

                              if (instance->ao_idx & 0x1)
                                    value <<= 16;

                              outl(value, instance->fifo_reg);
                        }
                  } else if ((*count <= ME4600_AO_CIRC_BUF_COUNT) &&
                           ((instance->
                             flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK)
                            == ME4600_AO_FLAGS_SW_WRAP_MODE_INF)) {
                        for (i = 0; i < *count; i++) {
                              if (get_user(value, values + i)) {
                                    PERROR
                                        ("Cannot copy value from user space.\n");
                                    err = ME_ERRNO_INTERNAL;
                                    goto ERROR;
                              }

                              instance->circ_buf.buf[i] = value;  /* Used to hold the values. */
                        }

                        instance->circ_buf.tail = 0;  /* Used as the current read position. */
                        instance->circ_buf.head = *count;   /* Used as the buffer size. */

                        /* Preload the FIFO. */

                        for (i = 0; i < ME4600_AO_FIFO_COUNT;
                             i++, instance->circ_buf.tail++) {
                              if (instance->circ_buf.tail >=
                                  instance->circ_buf.head)
                                    instance->circ_buf.tail = 0;

                              if (instance->ao_idx & 0x1)
                                    outl(instance->circ_buf.
                                         buf[instance->circ_buf.
                                           tail] << 16,
                                         instance->fifo_reg);
                              else
                                    outl(instance->circ_buf.
                                         buf[instance->circ_buf.
                                           tail],
                                         instance->fifo_reg);
                        }
                  } else if ((*count <= ME4600_AO_CIRC_BUF_COUNT) &&
                           ((instance->
                             flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK)
                            == ME4600_AO_FLAGS_SW_WRAP_MODE_FIN)) {
                        unsigned int preload_count;

                        for (i = 0; i < *count; i++) {
                              if (get_user(value, values + i)) {
                                    PERROR
                                        ("Cannot copy value from user space.\n");
                                    err = ME_ERRNO_INTERNAL;
                                    goto ERROR;
                              }

                              instance->circ_buf.buf[i] = value;  /* Used to hold the values. */
                        }

                        instance->circ_buf.tail = 0;  /* Used as the current read position. */
                        instance->circ_buf.head = *count;   /* Used as the buffer size. */

                        /* Try to preload the whole FIFO. */
                        preload_count = ME4600_AO_FIFO_COUNT;

                        if (preload_count > instance->wrap_count)
                              preload_count = instance->wrap_count;

                        /* Preload the FIFO. */
                        for (i = 0; i < preload_count;
                             i++, instance->circ_buf.tail++) {
                              if (instance->circ_buf.tail >=
                                  instance->circ_buf.head)
                                    instance->circ_buf.tail = 0;

                              if (instance->ao_idx & 0x1)
                                    outl(instance->circ_buf.
                                         buf[instance->circ_buf.
                                           tail] << 16,
                                         instance->fifo_reg);
                              else
                                    outl(instance->circ_buf.
                                         buf[instance->circ_buf.
                                           tail],
                                         instance->fifo_reg);
                        }

                        instance->wrap_remaining =
                            instance->wrap_count - preload_count;
                  } else {
                        PERROR("To many values written.\n");
                        err = ME_ERRNO_INVALID_VALUE_COUNT;
                        goto ERROR;
                  }
            }

            break;

      case 2:           // Continuous mode
            /* Check if in SW wrapround mode */
            if (instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) {
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);
                  PERROR("Subdevice is configured SW wrapround mode.\n");
                  err = ME_ERRNO_PREVIOUS_CONFIG;
                  goto ERROR;
            }

            switch (write_mode) {

            case ME_WRITE_MODE_BLOCKING:
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  PDEBUG("Write for blocking continuous mode.\n");

                  while (cnt > 0) {
                        wait_event_interruptible(instance->wait_queue,
                                           (c =
                                            me_circ_buf_space_to_end
                                            (&instance->
                                             circ_buf)));

                        if (instance->
                            flags & ME4600_AO_FLAGS_BROKEN_PIPE) {
                              PERROR
                                  ("Broken pipe in blocking write.\n");
                              err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
                              goto ERROR;
                        } else if (signal_pending(current)) {
                              PERROR
                                  ("Wait for free buffer interrupted from signal.\n");
                              err = ME_ERRNO_SIGNAL;
                              goto ERROR;
                        }

                        PDEBUG("Space to end = %d.\n", c);

                        /* Only able to write size of free buffer or size of count */

                        if (cnt < c)
                              c = cnt;
                        k = sizeof(int) * c;
                        k -= copy_from_user(instance->circ_buf.buf +
                                        instance->circ_buf.head,
                                        values, k);
                        c = k / sizeof(int);

                        PDEBUG("Copy %d values from user space.\n", c);

                        if (!c) {
                              PERROR
                                  ("Cannot copy values from user space.\n");
                              err = ME_ERRNO_INTERNAL;
                              goto ERROR;
                        }

                        instance->circ_buf.head =
                            (instance->circ_buf.head +
                             c) & (instance->circ_buf.mask);

                        values += c;
                        cnt -= c;
                        ret += c;

                        /* Values are now available so enable interrupts */
                        spin_lock_irqsave(&instance->subdevice_lock,
                                      cpu_flags);

                        if (me_circ_buf_space(&instance->circ_buf)) {
                              tmp = inl(instance->ctrl_reg);
                              tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                              outl(tmp, instance->ctrl_reg);
                        }

                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                  }

                  *count = ret;

                  break;

            case ME_WRITE_MODE_NONBLOCKING:
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  PDEBUG("Write for non blocking continuous mode.\n");

                  while (cnt > 0) {
                        if (instance->
                            flags & ME4600_AO_FLAGS_BROKEN_PIPE) {
                              PERROR
                                  ("ME4600:Broken pipe in nonblocking write.\n");
                              err = ME_ERRNO_SUBDEVICE_NOT_RUNNING;
                              goto ERROR;
                        }

                        c = me_circ_buf_space_to_end(&instance->
                                               circ_buf);

                        if (!c) {
                              PDEBUG
                                  ("Returning from nonblocking write.\n");
                              break;
                        }

                        PDEBUG("Space to end = %d.\n", c);

                        /* Only able to write size of free buffer or size of count */

                        if (cnt < c)
                              c = cnt;
                        k = sizeof(int) * c;
                        k -= copy_from_user(instance->circ_buf.buf +
                                        instance->circ_buf.head,
                                        values, k);
                        c = k / sizeof(int);

                        PDEBUG("Copy %d values from user space.\n", c);

                        if (!c) {
                              PERROR
                                  ("Cannot copy values from user space.\n");
                              err = ME_ERRNO_INTERNAL;
                              goto ERROR;
                        }

                        instance->circ_buf.head =
                            (instance->circ_buf.head +
                             c) & (instance->circ_buf.mask);

                        values += c;
                        cnt -= c;
                        ret += c;

                        /* Values are now available so enable interrupts */
                        spin_lock_irqsave(&instance->subdevice_lock,
                                      cpu_flags);

                        if (me_circ_buf_space(&instance->circ_buf)) {
                              tmp = inl(instance->ctrl_reg);
                              tmp |= ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                              outl(tmp, instance->ctrl_reg);
                        }

                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                  }

                  *count = ret;

                  break;

            case ME_WRITE_MODE_PRELOAD:
                  PDEBUG("Write for preload continuous mode.\n");

                  if ((inl(instance->status_reg) &
                       ME4600_AO_STATUS_BIT_FSM)) {
                        spin_unlock_irqrestore(&instance->
                                           subdevice_lock,
                                           cpu_flags);
                        PERROR
                            ("Can't Preload DAC FIFO while conversion is running.\n");
                        err = ME_ERRNO_SUBDEVICE_BUSY;
                        goto ERROR;
                  }

                  tmp = inl(instance->ctrl_reg);

                  tmp |=
                      ME4600_AO_CTRL_BIT_STOP |
                      ME4600_AO_CTRL_BIT_IMMEDIATE_STOP;
                  outl(tmp, instance->ctrl_reg);
                  tmp &=
                      ~(ME4600_AO_CTRL_BIT_ENABLE_FIFO |
                        ME4600_AO_CTRL_BIT_ENABLE_IRQ);
                  outl(tmp, instance->ctrl_reg);
                  tmp |= ME4600_AO_CTRL_BIT_ENABLE_FIFO;
                  outl(tmp, instance->ctrl_reg);

                  instance->circ_buf.head = 0;
                  instance->circ_buf.tail = 0;
                  instance->flags &= ~ME4600_AO_FLAGS_BROKEN_PIPE;

                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  c = ME4600_AO_FIFO_COUNT;

                  if (cnt < c)
                        c = cnt;

                  for (i = 0; i < c; i++) {
                        if (get_user(value, values)) {
                              PERROR
                                  ("Can't copy value from user space.\n");
                              err = ME_ERRNO_INTERNAL;
                              goto ERROR;
                        }

                        if (instance->ao_idx & 0x1)
                              value <<= 16;

                        outl(value, instance->fifo_reg);

                        values++;
                  }

                  cnt -= c;

                  ret += c;

                  PDEBUG("Wrote %d values to fifo.\n", c);

                  while (1) {
                        c = me_circ_buf_space_to_end(&instance->
                                               circ_buf);

                        if (c == 0)
                              break;

                        if (cnt < c)
                              c = cnt;

                        if (c <= 0)
                              break;

                        k = sizeof(int) * c;

                        k -= copy_from_user(instance->circ_buf.buf +
                                        instance->circ_buf.head,
                                        values, k);

                        c = k / sizeof(int);

                        PDEBUG("Wrote %d values to circular buffer.\n",
                               c);

                        if (!c) {
                              PERROR
                                  ("Can't copy values from user space.\n");
                              err = ME_ERRNO_INTERNAL;
                              goto ERROR;
                        }

                        instance->circ_buf.head =
                            (instance->circ_buf.head +
                             c) & (instance->circ_buf.mask);

                        values += c;
                        cnt -= c;
                        ret += c;
                  }

                  *count = ret;

                  break;

            default:
                  spin_unlock_irqrestore(&instance->subdevice_lock,
                                     cpu_flags);

                  PERROR("Invalid write mode specified.\n");

                  err = ME_ERRNO_INVALID_WRITE_MODE;

                  goto ERROR;
            }

            break;

      default:          // Single mode of invalid
            spin_unlock_irqrestore(&instance->subdevice_lock, cpu_flags);
            PERROR("Subdevice is configured in single mode.\n");
            err = ME_ERRNO_PREVIOUS_CONFIG;
            goto ERROR;
      }

ERROR:

      ME_SUBDEVICE_EXIT;

      return err;
}

static irqreturn_t me4600_ao_isr(int irq, void *dev_id)
{
      unsigned long tmp;
      int value;
      me4600_ao_subdevice_t *instance = dev_id;
      int i;
      int c = 0;
      int c1 = 0;

      if (irq != instance->irq) {
            PDEBUG("Incorrect interrupt num: %d.\n", irq);
            return IRQ_NONE;
      }

      if (!((0x1 << (instance->ao_idx + 3)) & inl(instance->irq_status_reg))) {
            return IRQ_NONE;
      }

      PDEBUG("executed.\n");

      tmp = inl(instance->status_reg);

      if (!(tmp & ME4600_AO_STATUS_BIT_EF) &&
          (tmp & ME4600_AO_STATUS_BIT_HF) &&
          (tmp & ME4600_AO_STATUS_BIT_HF)) {
            c = ME4600_AO_FIFO_COUNT;
            PDEBUG("Fifo empty.\n");
      } else if ((tmp & ME4600_AO_STATUS_BIT_EF) &&
               (tmp & ME4600_AO_STATUS_BIT_HF) &&
               (tmp & ME4600_AO_STATUS_BIT_HF)) {
            c = ME4600_AO_FIFO_COUNT / 2;
            PDEBUG("Fifo under half full.\n");
      } else {
            c = 0;
            PDEBUG("Fifo full.\n");
      }

      PDEBUG("Try to write 0x%04X values.\n", c);

      if ((instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
          ME4600_AO_FLAGS_SW_WRAP_MODE_INF) {
            while (c) {
                  c1 = c;

                  if (c1 > (instance->circ_buf.head - instance->circ_buf.tail))     /* Only up to the end of the buffer */
                        c1 = (instance->circ_buf.head -
                              instance->circ_buf.tail);

                  /* Write the values to the FIFO */
                  for (i = 0; i < c1; i++, instance->circ_buf.tail++, c--) {
                        if (instance->ao_idx & 0x1)
                              outl(instance->circ_buf.
                                   buf[instance->circ_buf.tail] << 16,
                                   instance->fifo_reg);
                        else
                              outl(instance->circ_buf.
                                   buf[instance->circ_buf.tail],
                                   instance->fifo_reg);
                  }

                  if (instance->circ_buf.tail >= instance->circ_buf.head)     /* Start from beginning */
                        instance->circ_buf.tail = 0;
            }

            spin_lock(&instance->subdevice_lock);

            tmp = inl(instance->ctrl_reg);
            tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(tmp, instance->ctrl_reg);
            tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(tmp, instance->ctrl_reg);

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
                  PERROR("Broken pipe.\n");
                  instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE;
                  tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(tmp, instance->ctrl_reg);
            }

            spin_unlock(&instance->subdevice_lock);
      } else if ((instance->flags & ME4600_AO_FLAGS_SW_WRAP_MODE_MASK) ==
               ME4600_AO_FLAGS_SW_WRAP_MODE_FIN) {
            while (c && instance->wrap_remaining) {
                  c1 = c;

                  if (c1 > (instance->circ_buf.head - instance->circ_buf.tail))     /* Only up to the end of the buffer */
                        c1 = (instance->circ_buf.head -
                              instance->circ_buf.tail);

                  if (c1 > instance->wrap_remaining)  /* Only up to count of user defined number of values */
                        c1 = instance->wrap_remaining;

                  /* Write the values to the FIFO */
                  for (i = 0; i < c1;
                       i++, instance->circ_buf.tail++, c--,
                       instance->wrap_remaining--) {
                        if (instance->ao_idx & 0x1)
                              outl(instance->circ_buf.
                                   buf[instance->circ_buf.tail] << 16,
                                   instance->fifo_reg);
                        else
                              outl(instance->circ_buf.
                                   buf[instance->circ_buf.tail],
                                   instance->fifo_reg);
                  }

                  if (instance->circ_buf.tail >= instance->circ_buf.head)     /* Start from beginning */
                        instance->circ_buf.tail = 0;
            }

            spin_lock(&instance->subdevice_lock);

            tmp = inl(instance->ctrl_reg);

            if (!instance->wrap_remaining) {
                  PDEBUG("Finite SW wraparound done.\n");
                  tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
            }

            tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ;

            outl(tmp, instance->ctrl_reg);
            tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(tmp, instance->ctrl_reg);

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
                  PERROR("Broken pipe.\n");
                  instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE;
                  tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(tmp, instance->ctrl_reg);
            }

            spin_unlock(&instance->subdevice_lock);

      } else {          /* Regular continuous mode */

            while (1) {
                  c1 = me_circ_buf_values_to_end(&instance->circ_buf);
                  PDEBUG("Values to end = %d.\n", c1);

                  if (c1 > c)
                        c1 = c;

                  if (c1 <= 0) {
                        PDEBUG("Work done or buffer empty.\n");
                        break;
                  }

                  if (instance->ao_idx & 0x1) {
                        for (i = 0; i < c1; i++) {
                              value =
                                  *(instance->circ_buf.buf +
                                    instance->circ_buf.tail +
                                    i) << 16;
                              outl(value, instance->fifo_reg);
                        }
                  } else
                        outsl(instance->fifo_reg,
                              instance->circ_buf.buf +
                              instance->circ_buf.tail, c1);

                  instance->circ_buf.tail =
                      (instance->circ_buf.tail +
                       c1) & (instance->circ_buf.mask);

                  PDEBUG("%d values wrote to port 0x%04X.\n", c1,
                         instance->fifo_reg);

                  c -= c1;
            }

            spin_lock(&instance->subdevice_lock);

            tmp = inl(instance->ctrl_reg);

            if (!me_circ_buf_values(&instance->circ_buf)) {
                  PDEBUG
                      ("Disable Interrupt because no values left in buffer.\n");
                  tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
            }

            tmp |= ME4600_AO_CTRL_BIT_RESET_IRQ;

            outl(tmp, instance->ctrl_reg);
            tmp &= ~ME4600_AO_CTRL_BIT_RESET_IRQ;
            outl(tmp, instance->ctrl_reg);

            if (!(inl(instance->status_reg) & ME4600_AO_STATUS_BIT_FSM)) {
                  PDEBUG("Broken pipe in me4600_ao_isr.\n");
                  instance->flags |= ME4600_AO_FLAGS_BROKEN_PIPE;
                  tmp &= ~ME4600_AO_CTRL_BIT_ENABLE_IRQ;
                  outl(tmp, instance->ctrl_reg);
            }

            spin_unlock(&instance->subdevice_lock);

            wake_up_interruptible(&instance->wait_queue);
      }

      return IRQ_HANDLED;
}

static void me4600_ao_destructor(struct me_subdevice *subdevice)
{
      me4600_ao_subdevice_t *instance;

      PDEBUG("executed.\n");

      instance = (me4600_ao_subdevice_t *) subdevice;

      free_irq(instance->irq, instance);
      kfree(instance->circ_buf.buf);
      me_subdevice_deinit(&instance->base);
      kfree(instance);
}

me4600_ao_subdevice_t *me4600_ao_constructor(uint32_t reg_base,
                                   spinlock_t *preload_reg_lock,
                                   uint32_t *preload_flags,
                                   int ao_idx, int fifo, int irq)
{
      me4600_ao_subdevice_t *subdevice;
      int err;

      PDEBUG("executed.\n");

      /* Allocate memory for subdevice instance */
      subdevice = kmalloc(sizeof(me4600_ao_subdevice_t), GFP_KERNEL);

      if (!subdevice) {
            PERROR("Cannot get memory for subdevice instance.\n");
            return NULL;
      }

      memset(subdevice, 0, sizeof(me4600_ao_subdevice_t));

      /* Initialize subdevice base class */
      err = me_subdevice_init(&subdevice->base);

      if (err) {
            PERROR("Cannot initialize subdevice base class instance.\n");
            kfree(subdevice);
            return NULL;
      }
      // Initialize spin locks.
      spin_lock_init(&subdevice->subdevice_lock);

      subdevice->preload_reg_lock = preload_reg_lock;
      subdevice->preload_flags = preload_flags;

      /* Allocate and initialize circular buffer */
      subdevice->circ_buf.mask = ME4600_AO_CIRC_BUF_COUNT - 1;
      subdevice->circ_buf.buf = kmalloc(ME4600_AO_CIRC_BUF_SIZE, GFP_KERNEL);

      if (!subdevice->circ_buf.buf) {
            PERROR("Cannot initialize subdevice base class instance.\n");
            me_subdevice_deinit((me_subdevice_t *) subdevice);
            kfree(subdevice);
            return NULL;
      }

      memset(subdevice->circ_buf.buf, 0, ME4600_AO_CIRC_BUF_SIZE);

      subdevice->circ_buf.head = 0;
      subdevice->circ_buf.tail = 0;

      /* Initialize wait queue */
      init_waitqueue_head(&subdevice->wait_queue);

      /* Initialize single value to 0V */
      subdevice->single_value = 0x8000;

      /* Store analog output index */
      subdevice->ao_idx = ao_idx;

      /* Store if analog output has fifo */
      subdevice->fifo = fifo;

      /* Initialize registers */

      if (ao_idx == 0) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_00_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_00_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_00_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_00_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_00_TIMER_REG;
            subdevice->reg_base = reg_base;

            if (inl(subdevice->reg_base + ME4600_AO_BOSCH_REG) == 0x20000) {
                  PINFO("Bosch firmware in use for channel 0.\n");
                  subdevice->bosch_fw = 1;
            } else {
                  subdevice->bosch_fw = 0;
            }
      } else if (ao_idx == 1) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_01_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_01_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_01_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_01_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_01_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bosch_fw = 0;
      } else if (ao_idx == 2) {
            subdevice->ctrl_reg = reg_base + ME4600_AO_02_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_02_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_02_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_02_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_02_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bosch_fw = 0;
      } else {
            subdevice->ctrl_reg = reg_base + ME4600_AO_03_CTRL_REG;
            subdevice->status_reg = reg_base + ME4600_AO_03_STATUS_REG;
            subdevice->fifo_reg = reg_base + ME4600_AO_03_FIFO_REG;
            subdevice->single_reg = reg_base + ME4600_AO_03_SINGLE_REG;
            subdevice->timer_reg = reg_base + ME4600_AO_03_TIMER_REG;
            subdevice->reg_base = reg_base;
            subdevice->bosch_fw = 0;
      }

      subdevice->irq_status_reg = reg_base + ME4600_IRQ_STATUS_REG;
      subdevice->preload_reg = reg_base + ME4600_AO_LOADSETREG_XX;

      /* Register interrupt service routine */
      subdevice->irq = irq;

      if (request_irq
          (subdevice->irq, me4600_ao_isr, IRQF_DISABLED | IRQF_SHARED,
           ME4600_NAME, subdevice)) {
            PERROR("Cannot get interrupt line.\n");
            me_subdevice_deinit((me_subdevice_t *) subdevice);
            kfree(subdevice->circ_buf.buf);
            kfree(subdevice);
            return NULL;
      }

      /* Override base class methods. */
      subdevice->base.me_subdevice_destructor = me4600_ao_destructor;
      subdevice->base.me_subdevice_io_reset_subdevice =
          me4600_ao_io_reset_subdevice;
      subdevice->base.me_subdevice_io_single_config =
          me4600_ao_io_single_config;
      subdevice->base.me_subdevice_io_single_read = me4600_ao_io_single_read;
      subdevice->base.me_subdevice_io_single_write =
          me4600_ao_io_single_write;
      subdevice->base.me_subdevice_io_stream_config =
          me4600_ao_io_stream_config;
      subdevice->base.me_subdevice_io_stream_new_values =
          me4600_ao_io_stream_new_values;
      subdevice->base.me_subdevice_io_stream_write =
          me4600_ao_io_stream_write;
      subdevice->base.me_subdevice_io_stream_start =
          me4600_ao_io_stream_start;
      subdevice->base.me_subdevice_io_stream_status =
          me4600_ao_io_stream_status;
      subdevice->base.me_subdevice_io_stream_stop = me4600_ao_io_stream_stop;
      subdevice->base.me_subdevice_query_number_channels =
          me4600_ao_query_number_channels;
      subdevice->base.me_subdevice_query_subdevice_type =
          me4600_ao_query_subdevice_type;
      subdevice->base.me_subdevice_query_subdevice_caps =
          me4600_ao_query_subdevice_caps;
      subdevice->base.me_subdevice_query_subdevice_caps_args =
          me4600_ao_query_subdevice_caps_args;
      subdevice->base.me_subdevice_query_range_by_min_max =
          me4600_ao_query_range_by_min_max;
      subdevice->base.me_subdevice_query_number_ranges =
          me4600_ao_query_number_ranges;
      subdevice->base.me_subdevice_query_range_info =
          me4600_ao_query_range_info;
      subdevice->base.me_subdevice_query_timer = me4600_ao_query_timer;

      return subdevice;
}

#endif // BOSCH

/* Common functions
*/

05789 static int me4600_ao_query_range_by_min_max(me_subdevice_t *subdevice,
                                  int unit,
                                  int *min,
                                  int *max, int *maxdata, int *range)
{
      me4600_ao_subdevice_t *instance;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if ((*max - *min) < 0) {
            PERROR("Invalid minimum and maximum values specified.\n");
            return ME_ERRNO_INVALID_MIN_MAX;
      }

      if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
            if ((*max <= (ME4600_AO_MAX_RANGE + 1000))
                && (*min >= ME4600_AO_MIN_RANGE)) {
                  *min = ME4600_AO_MIN_RANGE;
                  *max = ME4600_AO_MAX_RANGE;
                  *maxdata = ME4600_AO_MAX_DATA;
                  *range = 0;
            } else {
                  PERROR("No matching range available.\n");
                  return ME_ERRNO_NO_RANGE;
            }
      } else {
            PERROR("Invalid physical unit specified.\n");
            return ME_ERRNO_INVALID_UNIT;
      }

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_number_ranges(me_subdevice_t *subdevice,
                               int unit, int *count)
{
      me4600_ao_subdevice_t *instance;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if ((unit == ME_UNIT_VOLT) || (unit == ME_UNIT_ANY)) {
            *count = 1;
      } else {
            *count = 0;
      }

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_range_info(me_subdevice_t *subdevice,
                              int range,
                              int *unit,
                              int *min, int *max, int *maxdata)
{
      me4600_ao_subdevice_t *instance;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (range == 0) {
            *unit = ME_UNIT_VOLT;
            *min = ME4600_AO_MIN_RANGE;
            *max = ME4600_AO_MAX_RANGE;
            *maxdata = ME4600_AO_MAX_DATA;
      } else {
            PERROR("Invalid range number specified.\n");
            return ME_ERRNO_INVALID_RANGE;
      }

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_timer(me_subdevice_t *subdevice,
                         int timer,
                         int *base_frequency,
                         long long *min_ticks, long long *max_ticks)
{
      me4600_ao_subdevice_t *instance;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if ((timer != ME_TIMER_ACQ_START) && (timer != ME_TIMER_CONV_START)) {
            PERROR("Invalid timer specified.\n");
            return ME_ERRNO_INVALID_TIMER;
      }

      if (instance->fifo) {   //Streaming device.
            *base_frequency = ME4600_AO_BASE_FREQUENCY;
            if (timer == ME_TIMER_ACQ_START) {
                  *min_ticks = ME4600_AO_MIN_ACQ_TICKS;
                  *max_ticks = ME4600_AO_MAX_ACQ_TICKS;
            } else if (timer == ME_TIMER_CONV_START) {
                  *min_ticks = ME4600_AO_MIN_CHAN_TICKS;
                  *max_ticks = ME4600_AO_MAX_CHAN_TICKS;
            }
      } else {          //Not streaming device!
            *base_frequency = 0;
            *min_ticks = 0;
            *max_ticks = 0;
      }

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_number_channels(me_subdevice_t *subdevice,
                                 int *number)
{
      me4600_ao_subdevice_t *instance;
      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      *number = 1;

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_subdevice_type(me_subdevice_t *subdevice,
                                int *type, int *subtype)
{
      me4600_ao_subdevice_t *instance;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      *type = ME_TYPE_AO;
      *subtype = (instance->fifo) ? ME_SUBTYPE_STREAMING : ME_SUBTYPE_SINGLE;

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_subdevice_caps(me_subdevice_t *subdevice, int *caps)
{
      me4600_ao_subdevice_t *instance;
      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      *caps =
          ME_CAPS_AO_TRIG_SYNCHRONOUS | ((instance->fifo) ? ME_CAPS_AO_FIFO :
                                 ME_CAPS_NONE);

      return ME_ERRNO_SUCCESS;
}

static int me4600_ao_query_subdevice_caps_args(struct me_subdevice *subdevice,
                                     int cap, int *args, int count)
{
      me4600_ao_subdevice_t *instance;
      int err = ME_ERRNO_SUCCESS;

      instance = (me4600_ao_subdevice_t *) subdevice;

      PDEBUG("executed. idx=%d\n", instance->ao_idx);

      if (count != 1) {
            PERROR("Invalid capability argument count.\n");
            return ME_ERRNO_INVALID_CAP_ARG_COUNT;
      }

      switch (cap) {
      case ME_CAP_AI_FIFO_SIZE:
            args[0] = (instance->fifo) ? ME4600_AO_FIFO_COUNT : 0;
            break;

      case ME_CAP_AI_BUFFER_SIZE:
            args[0] =
                (instance->circ_buf.buf) ? ME4600_AO_CIRC_BUF_COUNT : 0;
            break;

      default:
            PERROR("Invalid capability.\n");
            err = ME_ERRNO_INVALID_CAP;
            args[0] = 0;
      }

      return err;
}

Generated by  Doxygen 1.6.0   Back to index