/**
 * collectd - src/barometer.c
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; only version 2.1 of the License is
 * applicable.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Authors:
 *   Tomas Menzl
 **/

#include "collectd.h"
#include "common.h"
#include "utils_cache.h"
#include "plugin.h"

#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <math.h>

/* ------------ MPL115 defines ------------ */
/* I2C address of the MPL115 sensor */
#define MPL115_I2C_ADDRESS          0x60
                                    
/* register addresses */            
#define MPL115_ADDR_CONV            0x00
#define MPL115_ADDR_COEFFS          0x04
                                    
/* register sizes */                
#define MPL115_NUM_CONV             4
#define MPL115_NUM_COEFFS           12
                                    
/* commands / addresses */          
#define MPL115_CMD_CONVERT_PRESS    0x10
#define MPL115_CMD_CONVERT_TEMP     0x11
#define MPL115_CMD_CONVERT_BOTH     0x12
                                    
#define MPL115_CONVERSION_RETRIES   5


/* ------------ MPL3115 defines ------------ */
/* MPL3115 I2C address */
#define MPL3115_I2C_ADDRESS         0x60

/* register addresses (only the interesting ones) */
#define MPL3115_REG_STATUS          0x00
#define MPL3115_REG_OUT_P_MSB       0x01
#define MPL3115_REG_OUT_P_CSB       0x02
#define MPL3115_REG_OUT_P_LSB       0x03
#define MPL3115_REG_OUT_T_MSB       0x04
#define MPL3115_REG_OUT_T_LSB       0x05
#define MPL3115_REG_DR_STATUS       0x06
#define MPL3115_REG_WHO_AM_I        0x0C
#define MPL3115_REG_SYSMOD          0x11
#define MPL3115_REG_PT_DATA_CFG     0x13
#define MPL3115_REG_BAR_IN_MSB      0x14
#define MPL3115_REG_BAR_IN_LSB      0x15
#define MPL3115_REG_CTRL_REG1       0x26
#define MPL3115_REG_CTRL_REG2       0x27
#define MPL3115_REG_CTRL_REG3       0x28
#define MPL3115_REG_CTRL_REG4       0x29
#define MPL3115_REG_CTRL_REG5       0x2A
#define MPL3115_REG_OFF_P           0x2B
#define MPL3115_REG_OFF_T           0x2C
#define MPL3115_REG_OFF_H           0x2D

/* Register values, masks */
#define MPL3115_WHO_AM_I_RESP       0xC4

#define MPL3115_PT_DATA_DREM        0x04
#define MPL3115_PT_DATA_PDEF        0x02
#define MPL3115_PT_DATA_TDEF        0x01
                                    
#define MPL3115_DR_STATUS_TDR       0x02
#define MPL3115_DR_STATUS_PDR       0x04
#define MPL3115_DR_STATUS_PTDR      0x08
#define MPL3115_DR_STATUS_DR        (MPL3115_DR_STATUS_TDR | MPL3115_DR_STATUS_PDR | MPL3115_DR_STATUS_PTDR)
                                    
#define MPL3115_DR_STATUS_TOW       0x20
#define MPL3115_DR_STATUS_POW       0x40
#define MPL3115_DR_STATUS_PTOW      0x80

#define MPL3115_CTRL_REG1_ALT       0x80
#define MPL3115_CTRL_REG1_RAW       0x40
#define MPL3115_CTRL_REG1_OST_MASK  0x38
#define MPL3115_CTRL_REG1_OST_1     0x00
#define MPL3115_CTRL_REG1_OST_2     0x08
#define MPL3115_CTRL_REG1_OST_4     0x10
#define MPL3115_CTRL_REG1_OST_8     0x18
#define MPL3115_CTRL_REG1_OST_16    0x20
#define MPL3115_CTRL_REG1_OST_32    0x28
#define MPL3115_CTRL_REG1_OST_64    0x30
#define MPL3115_CTRL_REG1_OST_128   0x38
#define MPL3115_CTRL_REG1_RST       0x04
#define MPL3115_CTRL_REG1_OST       0x02
#define MPL3115_CTRL_REG1_SBYB      0x01
#define MPL3115_CTRL_REG1_SBYB_MASK 0xFE

#define MPL3115_NUM_CONV_VALS       5


/* ------------ BMP085 defines ------------ */
/* I2C address of the BMP085 sensor */
#define BMP085_I2C_ADDRESS          0x77

/* register addresses */            
#define BMP085_ADDR_ID_REG          0xD0
#define BMP085_ADDR_VERSION         0xD1

#define BMP085_ADDR_CONV            0xF6

#define BMP085_ADDR_CTRL_REG        0xF4
#define BMP085_ADDR_COEFFS          0xAA

/* register sizes */                
#define BMP085_NUM_COEFFS           22

/* commands, values */
#define BMP085_CHIP_ID              0x55

#define BMP085_CMD_CONVERT_TEMP     0x2E

#define BMP085_CMD_CONVERT_PRESS_0  0x34
#define BMP085_CMD_CONVERT_PRESS_1  0x74
#define BMP085_CMD_CONVERT_PRESS_2  0xB4
#define BMP085_CMD_CONVERT_PRESS_3  0xF4

/* in us */
#define BMP085_TIME_CNV_TEMP        4500

#define BMP085_TIME_CNV_PRESS_0     4500
#define BMP085_TIME_CNV_PRESS_1     7500
#define BMP085_TIME_CNV_PRESS_2    13500
#define BMP085_TIME_CNV_PRESS_3    25500


/* ------------ Normalization ------------ */
/* Mean sea level pressure normalization methods */
#define MSLP_NONE          0
#define MSLP_INTERNATIONAL 1
#define MSLP_DEU_WETT      2

/** Temperature reference history depth for averaging. See #get_reference_temperature */
#define REF_TEMP_AVG_NUM   5


/* ------------------------------------------ */

/** Supported sensor types */
enum Sensor_type {
    Sensor_none = 0,
    Sensor_MPL115,
    Sensor_MPL3115,
    Sensor_BMP085
};

static const char *config_keys[] =
{
    "Device",
    "Oversampling",
    "PressureOffset",    /**< only for MPL3115 */
    "TemperatureOffset", /**< only for MPL3115 */
    "Altitude",
    "Normalization",
    "TemperatureSensor"
};

static int    config_keys_num     = STATIC_ARRAY_SIZE(config_keys);
                                  
static char * config_device       = NULL;  /**< I2C bus device */
static int    config_oversample   = 1;     /**< averaging window */

static double config_press_offset = 0.0;   /**< pressure offset */
static double config_temp_offset  = 0.0;   /**< temperature offset */

static double config_altitude     = NAN;   /**< altitude */
static int    config_normalize    = 0;     /**< normalization method */
                                  
static _Bool  configured          = 0;     /**< the whole plugin config status */
                                  
static int    i2c_bus_fd          = -1;    /**< I2C bus device FD */

static enum Sensor_type sensor_type = Sensor_none; /**< detected/used sensor type */

static __s32  mpl3115_oversample  = 0;    /**< MPL3115 CTRL1 oversample setting */

// BMP085 configuration
static unsigned      bmp085_oversampling; /**< BMP085 oversampling (0-3) */
static unsigned long bmp085_timeCnvPress; /**< BMP085 conversion time for pressure in us */
static __u8          bmp085_cmdCnvPress;  /**< BMP085 pressure conversion command */


/* MPL115 conversion coefficients */
static double mpl115_coeffA0;
static double mpl115_coeffB1;
static double mpl115_coeffB2;
static double mpl115_coeffC12;
static double mpl115_coeffC11;
static double mpl115_coeffC22;

/* BMP085 conversion coefficients */
static short bmp085_AC1;
static short bmp085_AC2;
static short bmp085_AC3;
static unsigned short bmp085_AC4;
static unsigned short bmp085_AC5;
static unsigned short bmp085_AC6;
static short bmp085_B1;
static short bmp085_B2;
static short bmp085_MB;
static short bmp085_MC;
static short bmp085_MD;



/* ------------------------ averaging ring buffer ------------------------ */
/*  Used only for MPL115. MPL3115 supports real oversampling in the device so */
/*  no need for any postprocessing. */

static _Bool avg_initialized = 0;    /**< already initialized by real values */

typedef struct averaging_s {
    long int * ring_buffer;
    int        ring_buffer_size;
    long int   ring_buffer_sum;
    int        ring_buffer_head;
} averaging_t;


static averaging_t pressure_averaging    = { NULL, 0, 0L, 0 };
static averaging_t temperature_averaging = { NULL, 0, 0L, 0 };


/** 
 * Create / allocate averaging buffer
 *
 * The buffer is initialized with zeros.
 *
 * @param avg  pointer to ring buffer to be allocated
 * @param size requested buffer size
 *
 * @return Zero when successful
 */
static int averaging_create(averaging_t * avg, int size)
{
    int a;

    avg->ring_buffer = (long int *) malloc(size * sizeof(*avg));
    if (avg->ring_buffer == NULL)
    {
        ERROR ("barometer: averaging_create - ring buffer allocation of size %d failed",
               size);
        return -1;
    }

    for (a=0; a<size; ++a)
    {
      avg->ring_buffer[a] = 0L;
    }

    avg->ring_buffer_size = size;
    avg->ring_buffer_sum  = 0L;
    avg->ring_buffer_head = 0;

    return 0;
}


/**
 * Delete / free existing averaging buffer
 *
 * @param avg  pointer to the ring buffer to be deleted
 */
static void averaging_delete(averaging_t * avg)
{
    if (avg->ring_buffer != NULL)
    {
        free(avg->ring_buffer);
        avg->ring_buffer = NULL;
    }
    avg->ring_buffer_size = 0;
    avg->ring_buffer_sum  = 0L;
    avg->ring_buffer_head = 0;
}


/*
 * Add new sample to the averaging buffer
 *
 * A new averaged value is returned. Note that till the buffer is full
 * returned value is inaccurate as it is an average of real values and initial
 * zeros.
 *
 * @param avg    pointer to the ring buffer
 * @param sample new sample value
 *
 * @return Averaged sample value
 */
static double averaging_add_sample(averaging_t * avg, long int sample)
{
    double result;

    avg->ring_buffer_sum += sample - avg->ring_buffer[avg->ring_buffer_head];
    avg->ring_buffer[avg->ring_buffer_head] = sample;
    avg->ring_buffer_head = (avg->ring_buffer_head+1) % avg->ring_buffer_size;
    result = (double)(avg->ring_buffer_sum) / (double)(avg->ring_buffer_size);
    
    DEBUG ("barometer: averaging_add_sample - added %ld, result = %lf", 
           sample, 
           result);

    return result;
}


/* ------------------------ temperature refference ------------------------ */

/**
 * Linked list type of temperature sensor references
 */
typedef struct temperature_list_s {
    char                      * sensor_name; /**< sensor name/reference */
    size_t                      num_values;  /**< number of values (usually one) */
    _Bool                       initialized; /**< sensor already provides data */
    struct temperature_list_s * next;        /**< next in the list */
} temperature_list_t;

static temperature_list_t * temp_list = NULL;


/*
 * Add new sensor to the temperature reference list
 *
 * @param list   the list
 * @param sensor reference name (as provided by the config file)
 *
 * @return Zero when successful
 */
static int temp_list_add(temperature_list_t * list, const char * sensor)
{
    temperature_list_t * new_temp;

    new_temp = (temperature_list_t *) malloc(sizeof(*new_temp));
    if(new_temp == NULL)
        return -1;

    new_temp->sensor_name = strdup(sensor);
    new_temp->initialized = 0;
    new_temp->num_values = 0;
    if(new_temp->sensor_name == NULL)
    {
        free(new_temp);
        return -1;
    }

    new_temp->next = temp_list;
    temp_list = new_temp;
    return 0;
}


/*
 * Delete the whole temperature reference list
 *
 * @param list the list to be deleted
 */
static void temp_list_delete(temperature_list_t ** list)
{
    temperature_list_t * tmp;

    while (*list != NULL)
    {
        tmp = (*list);
        (*list) = (*list)->next;
        free(tmp->sensor_name);
        free(tmp);
        tmp = NULL;
    }
}


/*
 * Get reference temperature value
 *
 * First initially uc_get_rate_by_name is tried. At the startup due to nondeterministic
 * order the temperature may not be read yet (then it fails and first measurment gives
 * only absolute air pressure reading which is acceptable). Once it succedes (should be
 * second measurement at the latest) we use average of few last readings from
 * uc_get_history_by_name. It may take few readings to start filling so again we use
 * uc_get_rate_by_name as a fallback.
 * The idea is to use basic "noise" filtering (history averaging) across all the values
 * which given sensor provides (up to given depth). Then we get minimum among
 * the sensors.
 *
 * @param result where the result is stored. When not available NAN is stored.
 *
 * @return Zero when successful
 */
static int get_reference_temperature(double * result)
{
    temperature_list_t * list = temp_list;

    gauge_t * values = NULL;   /**< rate values */
    size_t    values_num = 0;  /**< number of rate values */
    int i;

    gauge_t values_history[REF_TEMP_AVG_NUM];

    double avg_sum;  /**< Value sum for computing average */
    int    avg_num;  /**< Number of values for computing average */
    double average;  /**< Resulting value average */

    *result = NAN;

    while(list != NULL)
    {
        avg_sum = 0.0;
        avg_num = 0;

        /* First time need to read current rate to learn how many values are
           there (typically for temperature it would be just one).
           We do not expect dynamic changing of number of temperarure values
           in runtime yet (are there any such cases?). */
        if(!list->initialized)
        {
            if(uc_get_rate_by_name(list->sensor_name,
                                   &values,
                                   &values_num))
            {
                DEBUG ("barometer: get_reference_temperature - rate \"%s\" not found yet",
                       list->sensor_name);
                list = list->next;
                continue;
            }

            DEBUG ("barometer: get_reference_temperature - initialize \"%s\", %zu vals",
                   list->sensor_name,
                   values_num);

            list->initialized = 1;
            list->num_values = values_num;

            for(i=0; i<values_num; ++i)
            {
                DEBUG ("barometer: get_reference_temperature - rate %d: %lf **",
                       i,
                       values[i]);
                if(!isnan(values[i]))
                {
                    avg_sum += values[i];
                    ++avg_num;
                }
            }
            free(values);
            values = NULL;
        }

        /* It is OK to get here the first time as well, in the worst case
           the history will full of NANs. */
        if(uc_get_history_by_name(list->sensor_name,
                                  values_history,
                                  REF_TEMP_AVG_NUM,
                                  list->num_values))
        {
            ERROR ("barometer: get_reference_temperature - history \"%s\" lost",
                   list->sensor_name);
            list->initialized = 0;
            list->num_values = 0;
            list = list->next;
            continue;
        }
            
        for(i=0; i<REF_TEMP_AVG_NUM*list->num_values; ++i)
        {
            DEBUG ("barometer: get_reference_temperature - history %d: %lf",
                   i,
                   values_history[i]);
            if(!isnan(values_history[i]))
            {
                avg_sum += values_history[i];
                ++avg_num;
            }
        }

        if(avg_num == 0)   /* still no history? fallback to current */
        {
            if(uc_get_rate_by_name(list->sensor_name,
                                   &values,
                                   &values_num))
            {
                ERROR ("barometer: get_reference_temperature - rate \"%s\" lost",
                       list->sensor_name);
                list->initialized = 0;
                list->num_values = 0;
                list = list->next;
                continue;
            }

            for(i=0; i<values_num; ++i)
            {
                DEBUG ("barometer: get_reference_temperature - rate last %d: %lf **",
                       i,
                       values[i]);
                if(!isnan(values[i]))
                {
                    avg_sum += values[i];
                    ++avg_num;
                }
            }
            free(values);
            values = NULL;
        }

        if(avg_num == 0)
        {
            ERROR ("barometer: get_reference_temperature - could not read \"%s\"",
                   list->sensor_name);
            list->initialized = 0;
            list->num_values = 0;
        }
        else
        {
            average = avg_sum / (double) avg_num;
            if(isnan(*result))
                *result=average;
            else if(*result>average)
                *result=average;
        }
        list = list->next;
    }  /* while sensor list */
    
    if(*result == NAN)
    {
        ERROR("barometer: get_reference_temperature - no sensor available (yet?)");
        return -1;
    }
    DEBUG ("barometer: get_reference_temperature - temp is %lf", *result);
    return 0;
}


/* ------------------------ MPL115 access ------------------------ */

/** 
 * Detect presence of a MPL115 pressure sensor.
 *
 * Unfortunately there seems to be no ID register so we just try to read first
 * conversion coefficient from device at MPL115 address and hope it is really
 * MPL115. We should use this check as the last resort (which would be the typical
 * case anyway since MPL115 is the least accurate sensor).
 * As a sideeffect will leave set I2C slave address.
 * 
 * @return 1 if MPL115, 0 otherwise
 */
static int MPL115_detect(void)
{
    __s32 res;
    char errbuf[1024];

    if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL115_I2C_ADDRESS) < 0)
    {
        ERROR("barometer: MPL115_detect problem setting i2c slave address to 0x%02X: %s",
              MPL115_I2C_ADDRESS,
              sstrerror (errno, errbuf, sizeof (errbuf)));
        return 0 ;
    }

    res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL115_ADDR_COEFFS);
    if(res >= 0)
    {
        DEBUG ("barometer: MPL115_detect - positive detection");
        return 1;
    }

    DEBUG ("barometer: MPL115_detect - negative detection");
    return 0;
}

/** 
 * Read the MPL115 sensor conversion coefficients.
 *
 * These are (device specific) constants so we can read them just once.
 *
 * @return Zero when successful
 */
static int MPL115_read_coeffs(void)
{
    uint8_t mpl115_coeffs[MPL115_NUM_COEFFS]; 
    int32_t res;

    int8_t  sia0MSB, sia0LSB, sib1MSB, sib1LSB, sib2MSB, sib2LSB;
    int8_t  sic12MSB, sic12LSB, sic11MSB, sic11LSB, sic22MSB, sic22LSB;
    int16_t sia0, sib1, sib2, sic12, sic11, sic22;
      
    char errbuf[1024];

    res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, 
                                        MPL115_ADDR_COEFFS, 
                                        MPL115_NUM_COEFFS, 
                                        mpl115_coeffs);
    if (res < 0)
    {
        ERROR ("barometer: MPL115_read_coeffs - problem reading data: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }
   
    /* Using perhaps less elegant/efficient code, but more readable. */
    /* a0: 16total 1sign 12int 4fract 0pad */
    sia0MSB = mpl115_coeffs[0];
    sia0LSB = mpl115_coeffs[1];
    sia0 = (int16_t) sia0MSB <<8;          /* s16 type, Shift to MSB */
    sia0 += (int16_t) sia0LSB & 0x00FF;    /* Add LSB to 16bit number */
    mpl115_coeffA0 = (double) (sia0);
    mpl115_coeffA0 /= 8.0;                 /* 3 fract bits */
    
    /* b1: 16total 1sign 2int 13fract 0pad */
    sib1MSB= mpl115_coeffs[2];
    sib1LSB= mpl115_coeffs[3];
    sib1 = sib1MSB <<8;                    /* Shift to MSB */
    sib1 += sib1LSB & 0x00FF;              /* Add LSB to 16bit number */
    mpl115_coeffB1 = (double) (sib1);
    mpl115_coeffB1 /= 8192.0;              /* 13 fract */
    
    /* b2: 16total 1sign 1int 14fract 0pad */
    sib2MSB= mpl115_coeffs[4];
    sib2LSB= mpl115_coeffs[5];
    sib2 = sib2MSB <<8;                     /* Shift to MSB */
    sib2 += sib2LSB & 0x00FF;               /* Add LSB to 16bit number */
    mpl115_coeffB2 = (double) (sib2);
    mpl115_coeffB2 /= 16384.0;              /* 14 fract */

    /* c12: 14total 1sign 0int 13fract 9pad */
    sic12MSB= mpl115_coeffs[6];
    sic12LSB= mpl115_coeffs[7];
    sic12 = sic12MSB <<8;                   /* Shift to MSB only by 8 for MSB */
    sic12 += sic12LSB & 0x00FF;
    mpl115_coeffC12 = (double) (sic12);
    mpl115_coeffC12 /= 4.0;                 /* 16-14=2 */
    mpl115_coeffC12 /= 4194304.0;           /* 13+9=22 fract */

    /* c11: 11total 1sign 0int 11fract 11pad */
    sic11MSB= mpl115_coeffs[8];
    sic11LSB= mpl115_coeffs[9];
    sic11 = sic11MSB <<8;                   /* Shift to MSB only by 8 for MSB */
    sic11 += sic11LSB & 0x00FF;
    mpl115_coeffC11 = (double) (sic11);
    mpl115_coeffC11 /= 32.0;               /* 16-11=5 */
    mpl115_coeffC11 /= 4194304.0;          /* 11+11=22 fract */

    /* c12: 11total 1sign 0int 10fract 15pad */
    sic22MSB= mpl115_coeffs[10];
    sic22LSB= mpl115_coeffs[11];
    sic22 = sic22MSB <<8;                   /* Shift to MSB only by 8 for MSB */
    sic22 += sic22LSB & 0x00FF;
    mpl115_coeffC22 = (double) (sic22);
    mpl115_coeffC22 /= 32.0; //16-11=5
    mpl115_coeffC22 /= 33554432.0;          /* 10+15=25 fract */

    DEBUG("barometer: MPL115_read_coeffs: a0=%lf, b1=%lf, b2=%lf, c12=%lf, c11=%lf, c22=%lf",
          mpl115_coeffA0, 
          mpl115_coeffB1, 
          mpl115_coeffB2, 
          mpl115_coeffC12, 
          mpl115_coeffC11, 
          mpl115_coeffC22);
    return 0;
}


/**
 * Convert raw adc values to real data using the sensor coefficients.
 *
 * @param adc_pressure adc pressure value to be converted
 * @param adc_temp     adc temperature value to be converted
 * @param pressure     computed real pressure
 * @param temperature  computed real temperature
 */
static void MPL115_convert_adc_to_real(double   adc_pressure,
                                       double   adc_temp,
                                       double * pressure,
                                       double * temperature)
{
    double Pcomp;
    Pcomp = mpl115_coeffA0 +                                            \
        (mpl115_coeffB1 + mpl115_coeffC11*adc_pressure + mpl115_coeffC12*adc_temp) * adc_pressure + \
        (mpl115_coeffB2 + mpl115_coeffC22*adc_temp) * adc_temp;
    
    *pressure = ((1150.0-500.0) * Pcomp / 1023.0) + 500.0;
    *temperature = (472.0 - adc_temp) / 5.35 + 25.0;
    DEBUG ("barometer: MPL115_convert_adc_to_real - got %lf hPa, %lf C",
           *pressure,
           *temperature);
}


/** 
 * Read sensor averegaed measurements
 *
 * @param pressure    averaged measured pressure
 * @param temperature averaged measured temperature
 *
 * @return Zero when successful
 */
static int MPL115_read_averaged(double * pressure, double * temperature)
{
    uint8_t mpl115_conv[MPL115_NUM_CONV]; 
    int8_t  res;
    int     retries;
    int     conv_pressure;
    int     conv_temperature;
    double  adc_pressure;
    double  adc_temperature;
    char    errbuf[1024];

    *pressure    = 0.0;
    *temperature = 0.0;
   
    /* start conversion of both temp and presure */
    retries = MPL115_CONVERSION_RETRIES;
    while (retries>0)
    {
        /* write 1 to start conversion */
        res = i2c_smbus_write_byte_data (i2c_bus_fd,
                                         MPL115_CMD_CONVERT_BOTH,
                                         0x01);
        if (res >= 0)
            break;

        --retries;
        if(retries>0)
        {
            ERROR ("barometer: MPL115_read_averaged - requesting conversion: %s, " \
                   "will retry at most %d more times",
                   sstrerror (errno, errbuf, sizeof (errbuf)),
                   retries);
        }
        else
        {
            ERROR ("barometer: MPL115_read_averaged - requesting conversion: %s, "\
                   "too many failed retries",
                   sstrerror (errno, errbuf, sizeof (errbuf)));
            return -1;
        }
    }

    usleep (10000); /* wait 10ms for the conversion */

    retries=MPL115_CONVERSION_RETRIES;
    while (retries>0)
    {
        res = i2c_smbus_read_i2c_block_data(i2c_bus_fd,
                                            MPL115_ADDR_CONV,
                                            MPL115_NUM_CONV,
                                            mpl115_conv); 
        if (res >= 0)
            break;

        --retries;
        if (retries>0)
        {
            ERROR ("barometer: MPL115_read_averaged - reading conversion: %s, " \
                   "will retry at most %d more times",
                   sstrerror (errno, errbuf, sizeof (errbuf)),
                   retries);
        }
        else
        {
            ERROR ("barometer: MPL115_read_averaged - reading conversion: %s, " \
                   "too many failed retries",
                   sstrerror (errno, errbuf, sizeof (errbuf)));
            return -1;
        }
    }
    
    conv_pressure    = ((mpl115_conv[0] << 8) | mpl115_conv[1]) >> 6;
    conv_temperature = ((mpl115_conv[2] << 8) | mpl115_conv[3]) >> 6;
    DEBUG ("barometer: MPL115_read_averaged, raw pressure ADC value = %d, " \
           "raw temperature ADC value = %d",
           conv_pressure,
           conv_temperature);

    adc_pressure    = averaging_add_sample (&pressure_averaging, conv_pressure);
    adc_temperature = averaging_add_sample (&temperature_averaging, conv_temperature);

    MPL115_convert_adc_to_real(adc_pressure, adc_temperature, pressure, temperature);

    DEBUG ("barometer: MPL115_read_averaged - averaged ADC pressure = %lf / temperature = %lf, " \
           "real pressure = %lf hPa / temperature = %lf C",
           adc_pressure,
           adc_temperature,
           *pressure,
           *temperature);
    
    return 0;
}

/* ------------------------ MPL3115 access ------------------------ */

/** 
 * Detect presence of a MPL3115 pressure sensor by checking register "WHO AM I"
 *
 * As a sideeffect will leave set I2C slave address.
 * 
 * @return 1 if MPL3115, 0 otherwise
 */
static int MPL3115_detect(void)
{
    __s32 res;
    char errbuf[1024];

    if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, MPL3115_I2C_ADDRESS) < 0)
    {
        ERROR("barometer: MPL3115_detect problem setting i2c slave address to 0x%02X: %s",
              MPL3115_I2C_ADDRESS,
              sstrerror (errno, errbuf, sizeof (errbuf)));
        return 0 ;
    }

    res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_WHO_AM_I);
    if(res == MPL3115_WHO_AM_I_RESP)
    {
        DEBUG ("barometer: MPL3115_detect - positive detection");
        return 1;
    }

    DEBUG ("barometer: MPL3115_detect - negative detection");
    return 0;
}

/** 
 * Adjusts oversampling to values supported by MPL3115
 *
 * MPL3115 supports only power of 2 in the range 1 to 128. 
 */
static void MPL3115_adjust_oversampling(void)
{
    int new_val = 0;

    if(config_oversample > 100)
    {
        new_val = 128;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_128;
    }
    else if(config_oversample > 48)
    {
        new_val = 64;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_64;
    }
    else if(config_oversample > 24)
    {
        new_val = 32;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_32;
    }
    else if(config_oversample > 12)
    {
        new_val = 16;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_16;
    }
    else if(config_oversample > 6)
    {
        new_val = 8;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_8;
    }
    else if(config_oversample > 3)
    {
        new_val = 4;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_4;
    }
    else if(config_oversample > 1)
    {
        new_val = 2;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_2;
    }
    else
    {
        new_val = 1;
        mpl3115_oversample = MPL3115_CTRL_REG1_OST_1;
    }

    DEBUG("barometer: MPL3115_adjust_oversampling - correcting oversampling from %d to %d",
          config_oversample, 
          new_val);
    config_oversample = new_val;
}

/** 
 * Read sensor averegaed measurements
 *
 * @param pressure    averaged measured pressure
 * @param temperature averaged measured temperature
 *
 * @return Zero when successful
 */
static int MPL3115_read(double * pressure, double * temperature)
{
    __s32 res;
    __s32 ctrl ;
    __u8 data[MPL3115_NUM_CONV_VALS];
    long int tmp_value = 0;
    char errbuf[1024];
    
    /* Set Active - activate the device from standby */
    res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_CTRL_REG1);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_read - cannot read CTRL_REG1: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }
    ctrl = res;
    res = i2c_smbus_write_byte_data(i2c_bus_fd, 
                                    MPL3115_REG_CTRL_REG1, 
                                    ctrl | MPL3115_CTRL_REG1_SBYB);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_read - problem activating: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }
    
    /* base sleep is 5ms x OST */
    usleep(5000 * config_oversample);
      
    /* check the flags/status if ready */
    res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_read - cannot read status register: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }
    
    while ((res & MPL3115_DR_STATUS_DR) != MPL3115_DR_STATUS_DR)
    {
        /* try some extra sleep... */
        usleep(10000);
        
        /* ... and repeat the check. The conversion has to finish sooner or later. */
        res = i2c_smbus_read_byte_data(i2c_bus_fd, MPL3115_REG_STATUS);
        if (res < 0)
        {
            ERROR ("barometer: MPL3115_read - cannot read status register: %s",
                   sstrerror (errno, errbuf, sizeof (errbuf)));
            return 1;
        }
    }
    
    /* Now read all the data in one block. There is address autoincrement. */
    res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, 
                                        MPL3115_REG_OUT_P_MSB, 
                                        MPL3115_NUM_CONV_VALS,
                                        data);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_read - cannot read data registers: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }
    
    tmp_value = (data[0] << 16) | (data[1] << 8) | data[2];
    *pressure = ((double) tmp_value) / 4.0 / 16.0 / 100.0;
    DEBUG ("barometer: MPL3115_read - absolute pressure = %lf hPa", *pressure);
    
    if(data[3] > 0x7F)
    {
        data[3] = ~data[3] + 1;
        *temperature = data[3];
        *temperature = - *temperature;
    }
    else
    {
        *temperature = data[3];
    }
    
    *temperature += (double)(data[4]) / 256.0;
    DEBUG ("barometer: MPL3115_read - temperature = %lf C", *temperature);
    
    return 0;
}

/** 
 * Initialize MPL3115 for barometeric measurements
 * 
 * @return 0 if successful
 */
static int MPL3115_init_sensor(void)
{
    __s32 res;
    __s8 offset;
    char errbuf[1024];
    
    /* Reset the sensor. It will reset immediately without ACKing */
    /* the transaction, so no error handling here. */
    i2c_smbus_write_byte_data(i2c_bus_fd, 
                              MPL3115_REG_CTRL_REG1, 
                              MPL3115_CTRL_REG1_RST);
    
    /* wait some time for the reset to finish */
    usleep(100000);

    /* now it should be in standby already so we can go and configure it */
    
    /*  Set temperature offset. */
    /*  result = ADCtemp + offset [C] */
    offset = (__s8) (config_temp_offset * 16.0);
    res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_OFF_T, offset);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_init_sensor - problem setting temp offset: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }
    
    /*  Set pressure offset. */
    /*  result = ADCpress + offset [hPa] */
    offset = (__s8) (config_press_offset * 100.0 / 4.0);
    res = i2c_smbus_write_byte_data(i2c_bus_fd, MPL3115_REG_OFF_P, offset);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_init_sensor - problem setting pressure offset: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }

    /* Enable Data Flags in PT_DATA_CFG - flags on both pressure and temp */
    res = i2c_smbus_write_byte_data(i2c_bus_fd, 
                                    MPL3115_REG_PT_DATA_CFG,
                                    MPL3115_PT_DATA_DREM        \
                                    | MPL3115_PT_DATA_PDEF      \
                                    | MPL3115_PT_DATA_TDEF);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_init_sensor - problem setting PT_DATA_CFG: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }

    /* Set to barometer with an OSR */ 
    res = i2c_smbus_write_byte_data(i2c_bus_fd, 
                                    MPL3115_REG_CTRL_REG1, 
                                    mpl3115_oversample);
    if (res < 0)
    {
        ERROR ("barometer: MPL3115_init_sensor - problem configuring CTRL_REG1: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }

    return 0;
}

/* ------------------------ BMP085 access ------------------------ */

/** 
 * Detect presence of a BMP085 pressure sensor by checking its ID register
 *
 * As a sideeffect will leave set I2C slave address.
 * 
 * @return 1 if BMP085, 0 otherwise
 */
static int BMP085_detect(void)
{
    __s32 res;
    char errbuf[1024];

    if (ioctl(i2c_bus_fd, I2C_SLAVE_FORCE, BMP085_I2C_ADDRESS) < 0)
    {
        ERROR("barometer: BMP085_detect - problem setting i2c slave address to 0x%02X: %s",
              BMP085_I2C_ADDRESS,
              sstrerror (errno, errbuf, sizeof (errbuf)));
        return 0 ;
    }

    res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_ID_REG);
    if(res == BMP085_CHIP_ID)
    {
        DEBUG ("barometer: BMP085_detect - positive detection");

        /* get version */
        res = i2c_smbus_read_byte_data(i2c_bus_fd, BMP085_ADDR_VERSION );
        if (res < 0)
        {
            ERROR("barometer: BMP085_detect - problem checking chip version: %s",
                  sstrerror (errno, errbuf, sizeof (errbuf)));
            return 0 ;
        }
        DEBUG ("barometer: BMP085_detect - chip version ML:0x%02X AL:0x%02X",
               res & 0x0f,
               (res & 0xf0) >> 4);
        return 1;
    }

    DEBUG ("barometer: BMP085_detect - negative detection");
    return 0;
}


/** 
 * Adjusts oversampling settings to values supported by BMP085
 *
 * BMP085 supports only 1,2,4 or 8 samples. 
 */
static void BMP085_adjust_oversampling(void)
{
    int new_val = 0;

    if( config_oversample > 6 ) /* 8 */
    {
        new_val = 8;
        bmp085_oversampling = 3;
        bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_3;
        bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_3;
    }
    else if( config_oversample > 3 ) /* 4 */
    {
        new_val = 4;
        bmp085_oversampling = 2;
        bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_2;
        bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_2;
    }
    else if( config_oversample > 1 ) /* 2 */
    {
        new_val = 2;
        bmp085_oversampling = 1;
        bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_1;
        bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_1;
    }
    else /* 1 */
    {
        new_val = 1;
        bmp085_oversampling = 0;
        bmp085_cmdCnvPress = BMP085_CMD_CONVERT_PRESS_0;
        bmp085_timeCnvPress = BMP085_TIME_CNV_PRESS_0;
    }

    DEBUG("barometer: BMP085_adjust_oversampling - correcting oversampling from %d to %d",
          config_oversample, 
          new_val);
    config_oversample = new_val;
}


/** 
 * Read the BMP085 sensor conversion coefficients.
 *
 * These are (device specific) constants so we can read them just once.
 *
 * @return Zero when successful
 */
static int BMP085_read_coeffs(void)
{
    __s32 res;
    __u8 coeffs[BMP085_NUM_COEFFS]; 
    char errbuf[1024];

    res = i2c_smbus_read_i2c_block_data(i2c_bus_fd, 
                                        BMP085_ADDR_COEFFS,
                                        BMP085_NUM_COEFFS, 
                                        coeffs);
    if (res < 0)
    {
        ERROR ("barometer: BMP085_read_coeffs - problem reading data: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }
    
    bmp085_AC1 = ((int16_t)  coeffs[0]  <<8) | (int16_t)  coeffs[1];
    bmp085_AC2 = ((int16_t)  coeffs[2]  <<8) | (int16_t)  coeffs[3];
    bmp085_AC3 = ((int16_t)  coeffs[4]  <<8) | (int16_t)  coeffs[5];
    bmp085_AC4 = ((uint16_t) coeffs[6]  <<8) | (uint16_t) coeffs[7];
    bmp085_AC5 = ((uint16_t) coeffs[8]  <<8) | (uint16_t) coeffs[9];
    bmp085_AC6 = ((uint16_t) coeffs[10] <<8) | (uint16_t) coeffs[11];
    bmp085_B1 =  ((int16_t)  coeffs[12] <<8) | (int16_t)  coeffs[13];
    bmp085_B2 =  ((int16_t)  coeffs[14] <<8) | (int16_t)  coeffs[15];
    bmp085_MB =  ((int16_t)  coeffs[16] <<8) | (int16_t)  coeffs[17];
    bmp085_MC =  ((int16_t)  coeffs[18] <<8) | (int16_t)  coeffs[19];
    bmp085_MD =  ((int16_t)  coeffs[20] <<8) | (int16_t)  coeffs[21];

    DEBUG("barometer: BMP085_read_coeffs - AC1=%d, AC2=%d, AC3=%d, AC4=%u,"\
          " AC5=%u, AC6=%u, B1=%d, B2=%d, MB=%d, MC=%d, MD=%d",
          bmp085_AC1,
          bmp085_AC2,
          bmp085_AC3,
          bmp085_AC4,
          bmp085_AC5,
          bmp085_AC6,
          bmp085_B1,
          bmp085_B2,
          bmp085_MB,
          bmp085_MC,
          bmp085_MD);

    return 0;
}


/**
 * Convert raw BMP085 adc values to real data using the sensor coefficients.
 *
 * @param adc_pressure adc pressure value to be converted
 * @param adc_temp     adc temperature value to be converted
 * @param pressure     computed real pressure
 * @param temperature  computed real temperature
 */
static void BMP085_convert_adc_to_real(long adc_pressure,
                                       long adc_temperature,
                                       double * pressure,
                                       double * temperature)

{
    long X1, X2, X3;
    long B3, B5, B6;
    unsigned long B4, B7;

    long T;
    long P;


    /* calculate real temperature */
    X1 = ( (adc_temperature - bmp085_AC6) * bmp085_AC5) >> 15;
    X2 = (bmp085_MC << 11) / (X1 + bmp085_MD);

    /* B5, T */
    B5 = X1 + X2;
    T = (B5 + 8) >> 4;
    *temperature = (double)T * 0.1;

    /* calculate real pressure */
    /* in general X1, X2, X3 are recycled while values of B3, B4, B5, B6 are kept */

    /* B6, B3 */
    B6 = B5 - 4000;
    X1 = ((bmp085_B2 * ((B6 * B6)>>12)) >> 11 );
    X2 = (((long)bmp085_AC2 * B6) >> 11);
    X3 = X1 + X2;
    B3 = (((((long)bmp085_AC1 * 4) + X3) << bmp085_oversampling) + 2) >> 2;
    
    /* B4 */
    X1 = (((long)bmp085_AC3*B6) >> 13);
    X2 = (bmp085_B1*((B6*B6) >> 12) ) >> 16;
    X3 = ((X1 + X2) + 2 ) >> 2;
    B4 = ((long)bmp085_AC4* (unsigned long)(X3 + 32768)) >> 15;
    
    /* B7, P */
    B7 =  (unsigned long)(adc_pressure - B3)*(50000>>bmp085_oversampling);
    if( B7 < 0x80000000 )
    {
        P = (B7 << 1) / B4;
    }
    else
    {
        P = (B7/B4) << 1;
    }
    X1 = (P >> 8) * (P >> 8);
    X1 = (X1 * 3038) >> 16;
    X2 = ((-7357) * P) >> 16;
    P = P + ( ( X1 + X2 + 3791 ) >> 4);
    
    *pressure = P / 100.0; // in [hPa] 
    DEBUG ("barometer: BMP085_convert_adc_to_real - got %lf hPa, %lf C",
           *pressure,
           *temperature);
}

    
/** 
 * Read compensated sensor measurements
 *
 * @param pressure    averaged measured pressure
 * @param temperature averaged measured temperature
 *
 * @return Zero when successful
 */
static int BMP085_read(double * pressure, double * temperature)
{
    __s32 res;
    __u8 measBuff[3];

    long adc_pressure;
    long adc_temperature;

    char errbuf[1024];

    /* start conversion of temperature */
    res = i2c_smbus_write_byte_data( i2c_bus_fd,
                                     BMP085_ADDR_CTRL_REG,
                                     BMP085_CMD_CONVERT_TEMP );
    if (res < 0)
    {
        ERROR ("barometer: BMP085_read - problem requesting temperature conversion: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }

    usleep(BMP085_TIME_CNV_TEMP); /* wait for the conversion */

    res = i2c_smbus_read_i2c_block_data( i2c_bus_fd,
                                         BMP085_ADDR_CONV, 
                                         2,
                                         measBuff); 
    if (res < 0)
    {
        ERROR ("barometer: BMP085_read - problem reading temperature data: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }

    adc_temperature = ( (unsigned short)measBuff[0] << 8 ) + measBuff[1]; 
    

    /* get presure */
    res = i2c_smbus_write_byte_data( i2c_bus_fd,
                                     BMP085_ADDR_CTRL_REG, 
                                     bmp085_cmdCnvPress );
    if (res < 0)
    {
        ERROR ("barometer: BMP085_read - problem requesting pressure conversion: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }

    usleep(bmp085_timeCnvPress); /* wait for the conversion */

    res = i2c_smbus_read_i2c_block_data( i2c_bus_fd,
                                         BMP085_ADDR_CONV, 
                                         3,
                                         measBuff );
    if (res < 0)
    {
        ERROR ("barometer: BMP085_read - problem reading pressure data: %s",
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return 1;
    }

    adc_pressure = (long)((((ulong)measBuff[0]<<16) | ((ulong)measBuff[1]<<8) | (ulong)measBuff[2] ) >> (8 - bmp085_oversampling));
    

    DEBUG ("barometer: BMP085_read - raw pressure ADC value = %ld, " \
           "raw temperature ADC value = %ld",
           adc_pressure,
           adc_temperature);

    BMP085_convert_adc_to_real(adc_pressure, adc_temperature, pressure, temperature);

    return 0;
}



/* ------------------------ Sensor detection ------------------------ */
/** 
 * Detect presence of a supported sensor.
 *
 * As a sideeffect will leave set I2C slave address.
 * The detection is done in the order BMP085, MPL3115, MPL115 and stops after
 * first sensor beeing found.
 * 
 * @return detected sensor type
 */
enum Sensor_type Detect_sensor_type(void)
{
    if(BMP085_detect())
        return Sensor_BMP085;

    else if(MPL3115_detect())
        return Sensor_MPL3115;

    else if(MPL115_detect())
        return Sensor_MPL115;

    return Sensor_none;
}

/* ------------------------ Common functionality ------------------------ */

/**
 * Convert absolute pressure (in hPa) to mean sea level pressure
 *
 * Implemented methods are:
 * - MSLP_NONE - no converions, returns absolute pressure
 *
 * - MSLP_INTERNATIONAL - see http://en.wikipedia.org/wiki/Atmospheric_pressure#Altitude_atmospheric_pressure_variation
 *           Requires #config_altitude
 *
 * - MSLP_DEU_WETT - formula as recommended by the Deutsche Wetterdienst. See 
 *                http://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Theorie
 *           Requires both #config_altitude and temperature reference(s).
 *
 * @param abs_pressure absloute pressure to be converted
 *
 * @return mean sea level pressure if successful, NAN otherwise
 */
static double abs_to_mean_sea_level_pressure(double abs_pressure)
{
    double mean = -1.0;
    double temp = 0.0;
    int result = 0;

    if (config_normalize >= MSLP_DEU_WETT)
    {
        result = get_reference_temperature(&temp);
        if(result)
        {
            return NAN;
        }
    }

    switch(config_normalize)
    {
    case MSLP_NONE:
        mean = abs_pressure;
        break;
        
    case MSLP_INTERNATIONAL:
        mean = abs_pressure / \
            pow(1.0 - 0.0065*config_altitude/288.15, 9.80665*0.0289644/(8.31447*0.0065));
        break;
        
    case MSLP_DEU_WETT:
    {
        double E; /* humidity */
        double x;
        if(temp<9.1)
            E = 5.6402 * (-0.0916 + exp(0.06*temp) );
        else
            E = 18.2194 * (1.0463 - exp(-0.0666*temp) );
        x = 9.80665 / (287.05 * (temp+273.15 + 0.12*E + 0.0065*config_altitude/2)) * config_altitude;
        mean = abs_pressure * exp(x);
    }
    break;

    default:
        ERROR ("barometer: abs_to_mean_sea_level_pressure: wrong conversion method %d", 
               config_normalize);
        mean = abs_pressure;
        break;
    }

    DEBUG ("barometer: abs_to_mean_sea_level_pressure: absPressure = %lf hPa, method = %d, meanPressure = %lf hPa",
           abs_pressure,
           config_normalize,
           mean);

    return mean; 
}

/* ------------------------ main plugin callbacks ------------------------ */

/** 
 * Main plugin configuration callback (using simple config)
 * 
 * @param key   configuration key we should process
 * @param value configuration value we should process
 * 
 * @return Zero when successful.
 */
static int collectd_barometer_config (const char *key, const char *value)
{
    DEBUG("barometer: collectd_barometer_config");

    if (strcasecmp (key, "Device") == 0)
    {
        sfree (config_device);
        config_device = strdup (value);
    }
    else if (strcasecmp (key, "Oversampling") == 0)
    {
        int oversampling_tmp = atoi (value);
        if (oversampling_tmp < 1 || oversampling_tmp > 1024)
        {
            WARNING ("barometer: collectd_barometer_config: invalid oversampling: %d." \
                     " Allowed values are 1 to 1024 (for MPL115) or 1 to 128 (for MPL3115) or 1 to 8 (for BMP085).",
                     oversampling_tmp);
            return 1;
        }
        config_oversample = oversampling_tmp;
    }
    else if (strcasecmp (key, "Altitude") == 0)
    {
        config_altitude = atof (value);
    }
    else if (strcasecmp (key, "Normalization") == 0)
    {
        int normalize_tmp = atoi (value);
        if (normalize_tmp < 0 || normalize_tmp > 2)
        {
            WARNING ("barometer: collectd_barometer_config: invalid normalization: %d",
                     normalize_tmp);
            return 1;
        }
        config_normalize = normalize_tmp;
    }
    else if (strcasecmp (key, "TemperatureSensor") == 0)
    {
        if(temp_list_add(temp_list, value))
        {
            return -1;
        }
    }
    else if (strcasecmp (key, "PressureOffset") == 0)
    {
        config_press_offset = atof(value);
    }
    else if (strcasecmp (key, "TemperatureOffset") == 0)
    {
        config_temp_offset = atof(value);
    }
    else 
    {
        return -1;
    }

    return 0;
}


/** 
 * Shutdown callback.
 * 
 * Close I2C and delete all the buffers.
 * 
 * @return Zero when successful (at the moment the only possible outcome)
 */
static int collectd_barometer_shutdown(void)
{
    DEBUG ("barometer: collectd_barometer_shutdown");

    if(sensor_type == Sensor_MPL115)
    {
        averaging_delete (&pressure_averaging);
        averaging_delete (&temperature_averaging);

        temp_list_delete(&temp_list);
    }

    if (i2c_bus_fd > 0)
    {
        close (i2c_bus_fd);
        i2c_bus_fd = -1;
        sfree (config_device);
    }

    return 0;
}


/** 
 * Plugin read callback for MPL115.
 * 
 *  Dispatching will create values:
 *  - <hostname>/barometer-mpl115/pressure-normalized
 *  - <hostname>/barometer-mpl115/pressure-absolute
 *  - <hostname>/barometer-mpl115/temperature
 *
 * @return Zero when successful.
 */
static int MPL115_collectd_barometer_read (void)
{
    int result = 0;

    double pressure        = 0.0;
    double temperature     = 0.0;
    double norm_pressure   = 0.0;

    value_list_t vl = VALUE_LIST_INIT;
    value_t      values[1];
    
    DEBUG("barometer: MPL115_collectd_barometer_read");

    if (!configured)
    {
        return -1;
    }

    /* Rather than delaying init, we will intitialize during first read. This
       way at least we have a better chance to have the reference temperature
       already available. */
    if(!avg_initialized)
    {
        int i;
        for(i=0; i<config_oversample-1; ++i)
        {
            result = MPL115_read_averaged(&pressure, &temperature);
            if(result)
            {
                ERROR ("barometer: MPL115_collectd_barometer_read - mpl115 read, ignored during init");
            }
            DEBUG("barometer: MPL115_collectd_barometer_read - init %d / %d", i+1, config_oversample-1);
            usleep(20000);
        }
        avg_initialized = 1;
    }

    result = MPL115_read_averaged(&pressure, &temperature);
    if(result)
        return result;

    norm_pressure = abs_to_mean_sea_level_pressure(pressure);

    sstrncpy (vl.host, hostname_g, sizeof (vl.host));
    sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin));
    sstrncpy (vl.plugin_instance, "mpl115", sizeof (vl.plugin_instance));

    vl.values_len = 1;
    vl.values = values;

    /* dispatch normalized air pressure */
    sstrncpy (vl.type, "pressure", sizeof (vl.type));
    sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance));
    values[0].gauge = norm_pressure;
    plugin_dispatch_values (&vl);

    /* dispatch absolute air pressure */
    sstrncpy (vl.type, "pressure", sizeof (vl.type));
    sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance));
    values[0].gauge = pressure;
    plugin_dispatch_values (&vl);

    /* dispatch sensor temperature */
    sstrncpy (vl.type, "temperature", sizeof (vl.type));
    sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
    values[0].gauge = temperature;
    plugin_dispatch_values (&vl);

    return 0;
}


/** 
 * Plugin read callback for MPL3115.
 * 
 *  Dispatching will create values:
 *  - <hostname>/barometer-mpl3115/pressure-normalized
 *  - <hostname>/barometer-mpl3115/pressure-absolute
 *  - <hostname>/barometer-mpl3115/temperature
 *
 * @return Zero when successful.
 */
static int MPL3115_collectd_barometer_read (void)
{
    int result = 0;
    
    double pressure        = 0.0;
    double temperature     = 0.0;
    double norm_pressure   = 0.0;
    
    value_list_t vl = VALUE_LIST_INIT;
    value_t      values[1];
    
    DEBUG("barometer: MPL3115_collectd_barometer_read");
    
    if (!configured)
    {
        return -1;
    }
    
    result = MPL3115_read(&pressure, &temperature);
    if(result)
        return result;

    norm_pressure = abs_to_mean_sea_level_pressure(pressure);

    sstrncpy (vl.host, hostname_g, sizeof (vl.host));
    sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin));
    sstrncpy (vl.plugin_instance, "mpl3115", sizeof (vl.plugin_instance));

    vl.values_len = 1;
    vl.values = values;

    /* dispatch normalized air pressure */
    sstrncpy (vl.type, "pressure", sizeof (vl.type));
    sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance));
    values[0].gauge = norm_pressure;
    plugin_dispatch_values (&vl);

    /* dispatch absolute air pressure */
    sstrncpy (vl.type, "pressure", sizeof (vl.type));
    sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance));
    values[0].gauge = pressure;
    plugin_dispatch_values (&vl);

    /* dispatch sensor temperature */
    sstrncpy (vl.type, "temperature", sizeof (vl.type));
    sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
    values[0].gauge = temperature;
    plugin_dispatch_values (&vl);

    return 0;
}


/** 
 * Plugin read callback for BMP085.
 * 
 *  Dispatching will create values:
 *  - <hostname>/barometer-bmp085/pressure-normalized
 *  - <hostname>/barometer-bmp085/pressure-absolute
 *  - <hostname>/barometer-bmp085/temperature
 *
 * @return Zero when successful.
 */
static int BMP085_collectd_barometer_read (void)
{
    int result = 0;
    
    double pressure        = 0.0;
    double temperature     = 0.0;
    double norm_pressure   = 0.0;
    
    value_list_t vl = VALUE_LIST_INIT;
    value_t      values[1];
    
    DEBUG("barometer: BMP085_collectd_barometer_read");
    
    if (!configured)
    {
        return -1;
    }
    
    result = BMP085_read(&pressure, &temperature);
    if(result)
        return result;

    norm_pressure = abs_to_mean_sea_level_pressure(pressure);

    sstrncpy (vl.host, hostname_g, sizeof (vl.host));
    sstrncpy (vl.plugin, "barometer", sizeof (vl.plugin));
    sstrncpy (vl.plugin_instance, "bmp085", sizeof (vl.plugin_instance));

    vl.values_len = 1;
    vl.values = values;

    /* dispatch normalized air pressure */
    sstrncpy (vl.type, "pressure", sizeof (vl.type));
    sstrncpy (vl.type_instance, "normalized", sizeof (vl.type_instance));
    values[0].gauge = norm_pressure;
    plugin_dispatch_values (&vl);

    /* dispatch absolute air pressure */
    sstrncpy (vl.type, "pressure", sizeof (vl.type));
    sstrncpy (vl.type_instance, "absolute", sizeof (vl.type_instance));
    values[0].gauge = pressure;
    plugin_dispatch_values (&vl);

    /* dispatch sensor temperature */
    sstrncpy (vl.type, "temperature", sizeof (vl.type));
    sstrncpy (vl.type_instance, "", sizeof (vl.type_instance));
    values[0].gauge = temperature;
    plugin_dispatch_values (&vl);

    return 0;
}


/** 
 * Initialization callback
 * 
 * Check config, initialize I2C bus access, conversion coefficients and averaging
 * ring buffers
 * 
 * @return Zero when successful.
 */
static int collectd_barometer_init (void)
{
    char errbuf[1024];

    DEBUG ("barometer: collectd_barometer_init");

    if (config_device == NULL)
    {
        ERROR("barometer: collectd_barometer_init I2C bus device not configured");
        return -1;
    }

    if (config_normalize >= MSLP_INTERNATIONAL && isnan(config_altitude))
    {
        ERROR("barometer: collectd_barometer_init no altitude configured " \
              "for mean sea level pressure normalization.");
        return -1;
    }

    if (config_normalize == MSLP_DEU_WETT
        &&
        temp_list == NULL)
    {
        ERROR("barometer: collectd_barometer_init no temperature reference "\
              "configured for mean sea level pressure normalization.");
        return -1;
    }


    i2c_bus_fd = open(config_device, O_RDWR);
    if (i2c_bus_fd < 0)
    {
        ERROR ("barometer: collectd_barometer_init problem opening I2C bus device \"%s\": %s (is loaded mod i2c-dev?)",
               config_device,
               sstrerror (errno, errbuf, sizeof (errbuf)));
        return -1;
    }

    /* detect sensor type - this will also set slave address */
    sensor_type = Detect_sensor_type();

    /* init correct sensor type */
    switch(sensor_type)
    {
/* MPL3115 */
    case Sensor_MPL3115:
    {
        MPL3115_adjust_oversampling();
        
        if(MPL3115_init_sensor())
            return -1;
        
        plugin_register_read ("barometer", MPL3115_collectd_barometer_read);
    }
    break;

/* MPL115 */
    case Sensor_MPL115:
    {
        if (averaging_create (&pressure_averaging, config_oversample))
        {
            ERROR("barometer: collectd_barometer_init pressure averaging init failed");
            return -1;
        }
        
        if (averaging_create (&temperature_averaging, config_oversample))
        {
            ERROR("barometer: collectd_barometer_init temperature averaging init failed");
            return -1;
        }
        
        if (MPL115_read_coeffs() < 0)
            return -1;
        
        plugin_register_read ("barometer", MPL115_collectd_barometer_read);
    }
    break;

/* BMP085 */
    case Sensor_BMP085:
    {
        BMP085_adjust_oversampling();

        if (BMP085_read_coeffs() < 0)
            return -1;

        plugin_register_read ("barometer", BMP085_collectd_barometer_read);
    }
    break;

/* anything else -> error */
    default:
        ERROR("barometer: collectd_barometer_init - no supported sensor found");
        return -1;
    }
        

    configured = 1;
    return 0;
}

/* ------------------------ plugin register / entry point ------------------------ */

/** 
 * Plugin "entry" - register all callback.
 * 
 */
void module_register (void)
{
    plugin_register_config ("barometer", 
                            collectd_barometer_config, 
                            config_keys, 
                            config_keys_num);
    plugin_register_init ("barometer", collectd_barometer_init);
    plugin_register_shutdown ("barometer", collectd_barometer_shutdown);
}
