#ifndef DAC_C
#define DAC_C

//----------------------------------
// Includes and forward declarations
//----------------------------------
#include <cyg/io/dac.h>
#include <cyg/io/devtab.h>
#include <cyg/io/io.h>

//--------------------
// Function prototypes
//--------------------
static Cyg_ErrNo dac_write(cyg_io_handle_t a_handle, 
                           const void * a_buf, 
			   cyg_uint32 * a_len);
static Cyg_ErrNo dac_set_config(cyg_io_handle_t a_handle, 
                                cyg_uint32 a_key, 
				const void * a_buf, 
				cyg_uint32 * a_len);

//-------------------------------------------
// Register the device driver with the kernel
//-------------------------------------------
DEVIO_TABLE(dac_devio,
            dac_write,
            0,
	    0,
            0,
            dac_set_config);

//-----------------------
// The callback functions
//-----------------------
DAC_CALLBACKS(dac_callbacks);

//-------------------------
// Function implementations
//-------------------------
// dac_write
static Cyg_ErrNo dac_write(cyg_io_handle_t a_handle, const void * a_buf, cyg_uint32 * a_len)
{
  cyg_devtab_entry_t * t = (cyg_devtab_entry_t *)a_handle;
  dac_channel * chan = (dac_channel *)t->priv;
  dac_funs * funs = chan->m_funs;

  double val = *(double *)a_buf;

  if(*a_len != sizeof(double))
    return -EINVAL;

  // Convert the value to avalue between -1.0 and 1.0
  val = (val * chan->m_scaling) + chan->m_offset; 

  // Avoid glitches !!
  val = val < -1.0 ? -1.0 : val;
  val = val > 1.0 ?   1.0 : val;

  funs->dac_write(chan, val);
 
  return ENOERR;
}

// dac_set_config
static Cyg_ErrNo dac_set_config(cyg_io_handle_t a_handle, cyg_uint32 a_key, const void * a_buf, cyg_uint32 * a_len)
{
  Cyg_ErrNo error = ENOERR;
  cyg_devtab_entry_t * t = (cyg_devtab_entry_t *)a_handle;
  dac_channel * chan = (dac_channel *)t->priv;

  switch(a_key)
  {
    case DAC_IO_SET_CONFIG_10V_FULL_SCALE:
      {
        chan->m_span = 5;
        chan->m_scaling = chan->m_scaling_usr * chan->m_scaling_cal / chan->m_span;
        chan->m_offset = chan->m_scaling_usr * (chan->m_offset_usr + chan->m_scaling_cal * chan->m_offset_cal) / chan->m_span;
      }
      break;

    case DAC_IO_SET_CONFIG_20V_FULL_SCALE:
      {
        chan->m_span = 10;
        chan->m_scaling = chan->m_scaling_usr * chan->m_scaling_cal / chan->m_span;
        chan->m_offset = chan->m_scaling_usr * (chan->m_offset_usr + chan->m_scaling_cal * chan->m_offset_cal) / chan->m_span;
      }
      break;

    case DAC_IO_SET_CONFIG_SCALING:
      {
        if(*a_len != sizeof(double))
          return -EINVAL;

        chan->m_scaling_usr = *(double *)a_buf;
        chan->m_scaling = chan->m_scaling_usr * chan->m_scaling_cal / chan->m_span;
        chan->m_offset = chan->m_scaling_usr * (chan->m_offset_usr + chan->m_scaling_cal * chan->m_offset_cal) / chan->m_span;
      }
      break;

    case DAC_IO_SET_CONFIG_OFFSET:
      {
        if(*a_len != sizeof(double))
          return -EINVAL;

        chan->m_offset_usr = *(double *)a_buf;
        chan->m_scaling = chan->m_scaling_usr * chan->m_scaling_cal / chan->m_span;
        chan->m_offset = chan->m_scaling_usr * (chan->m_offset_usr + chan->m_scaling_cal * chan->m_offset_cal) / chan->m_span;
      }
      break;

    case DAC_IO_SET_CONFIG_SCALING_CAL:
      {
        if(*a_len != sizeof(double))
          return -EINVAL;

        chan->m_scaling_cal = *(double *)a_buf;
        chan->m_scaling = chan->m_scaling_usr * chan->m_scaling_cal / chan->m_span;
        chan->m_offset = chan->m_scaling_usr * (chan->m_offset_usr + chan->m_scaling_cal * chan->m_offset_cal) / chan->m_span;
      }
      break;

    case DAC_IO_SET_CONFIG_OFFSET_CAL:
      {
        if(*a_len != sizeof(double))
          return -EINVAL;

        chan->m_offset_cal = *(double *)a_buf;
        chan->m_scaling = chan->m_scaling_usr * chan->m_scaling_cal / chan->m_span;
        chan->m_offset = chan->m_scaling_usr * (chan->m_offset_usr + chan->m_scaling_cal * chan->m_offset_cal) / chan->m_span;
      }
      break;

    default:
      error = -EINVAL;
      break;
  }

  return error;
}

#endif // DAC_C
