/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <string.h>
#include <errno.h>
#include <assert.h>

#include "os/mynewt.h"
#include "hal/hal_i2c.h"
#include "i2cn/i2cn.h"
#include "sensor/sensor.h"
#include "sensor/accel.h"
#include "sensor/mag.h"
#include "sensor/quat.h"
#include "sensor/euler.h"
#include "sensor/gyro.h"
#include "sensor/temperature.h"
#include "bno055/bno055.h"
#include "bno055_priv.h"
#include "modlog/modlog.h"
#include "stats/stats.h"
#include <syscfg/syscfg.h>

/* Define the stats section and records */
STATS_SECT_START(bno055_stat_section)
    STATS_SECT_ENTRY(errors)
STATS_SECT_END

/* Define stat names for querying */
STATS_NAME_START(bno055_stat_section)
    STATS_NAME(bno055_stat_section, errors)
STATS_NAME_END(bno055_stat_section)

/* Global variable used to hold stats data */
STATS_SECT_DECL(bno055_stat_section) g_bno055stats;

/* Exports for the sensor API.*/
static int bno055_sensor_read(struct sensor *, sensor_type_t,
        sensor_data_func_t, void *, uint32_t);
static int bno055_sensor_get_config(struct sensor *, sensor_type_t,
        struct sensor_cfg *);

static const struct sensor_driver g_bno055_sensor_driver = {
    bno055_sensor_read,
    bno055_sensor_get_config
};

/**
 * Writes a single byte to the specified register
 *
 * @param The Sesnsor interface
 * @param The register address to write to
 * @param The value to write
 *
 * @return 0 on success, non-zero error on failure.
 */
int
bno055_write8(struct sensor_itf *itf, uint8_t reg, uint8_t value)
{
    int rc;
    uint8_t payload[2] = { reg, value};

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    rc = bus_node_simple_write(itf->si_dev, payload, 2);
    if (rc) {
        BNO055_LOG_ERROR("Write to bno055 failed %d\n", rc);
        STATS_INC(g_bno055stats, errors);
    }
#else
    struct hal_i2c_master_data data_struct = {
        .address = itf->si_addr,
        .len = 2,
        .buffer = payload
    };

    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC, 1,
                           MYNEWT_VAL(BNO055_I2C_RETRIES));
    if (rc) {
        BNO055_LOG_ERROR(
                   "Failed to write to 0x%02X:0x%02X with value 0x%02X\n",
                   data_struct.address, reg, value);
        STATS_INC(g_bno055stats, errors);
    }
#endif

    return rc;
}

/**
 * Writes a multiple bytes to the specified register  (MAX: 22 bytes)
 *
 * @param The Sesnsor interface
 * @param The register address to write to
 * @param The data buffer to write from
 *
 * @return 0 on success, non-zero error on failure.
 */
int
bno055_writelen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
                uint8_t len)
{
    int rc;
    uint8_t payload[23] = { reg, 0, 0, 0, 0, 0, 0, 0,
                              0, 0, 0, 0, 0, 0, 0, 0,
                              0, 0, 0, 0, 0, 0, 0};

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    if (len > (sizeof(payload) - 1)) {
        return OS_EINVAL;
    }

    memcpy(payload, buffer, len);

    rc = bus_node_simple_write(itf->si_dev, payload, len + 1);
    if (rc) {
        BNO055_LOG_ERROR("Write to bno055 failed %d\n", rc);
        STATS_INC(g_bno055stats, errors);
    }
#else
    struct hal_i2c_master_data data_struct = {
        .address = itf->si_addr,
        .len = 1,
        .buffer = payload
    };

    if (len > (sizeof(payload) - 1)) {
        return OS_EINVAL;
    }

    memcpy(&payload[1], buffer, len);

    rc = sensor_itf_lock(itf, MYNEWT_VAL(BNO055_ITF_LOCK_TMO));
    if (rc) {
        return rc;
    }

    /* Register write */
    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                           MYNEWT_VAL(BNO055_I2C_RETRIES));
    if (rc) {
        BNO055_LOG_ERROR("I2C access failed at address 0x%02X\n",
                   data_struct.address);
        STATS_INC(g_bno055stats, errors);
        goto err;
    }

    memset(payload, 0, sizeof(payload));
    data_struct.len = len;
    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10,
                           len, MYNEWT_VAL(BNO055_I2C_RETRIES));
    if (rc) {
        BNO055_LOG_ERROR("Failed to read from 0x%02X:0x%02X\n",
                   data_struct.address, reg);
        STATS_INC(g_bno055stats, errors);;
    }

err:
    sensor_itf_unlock(itf);
#endif

    return rc;
}

/**
 * Reads a single byte from the specified register
 *
 * @param The Sensor interface
 * @param The register address to read from
 * @param Pointer to where the register value should be written
 *
 * @return 0 on success, non-zero error on failure.
 */
int
bno055_read8(struct sensor_itf *itf, uint8_t reg, uint8_t *value)
{
    int rc;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    rc = bus_node_simple_write_read_transact(itf->si_dev, &reg, 1, value, 1);
    if (rc) {
        BNO055_LOG_ERROR("Read from bno055 failed %d\n", rc);
        STATS_INC(g_bno055stats, errors);
    }
#else
    uint8_t payload;
    struct hal_i2c_master_data data_struct = {
        .address = itf->si_addr,
        .len = 1,
        .buffer = &payload
    };

    rc = sensor_itf_lock(itf, MYNEWT_VAL(BNO055_ITF_LOCK_TMO));
    if (rc) {
        return rc;
    }

    /* Register write */
    payload = reg;
    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 0,
                           MYNEWT_VAL(BNO055_I2C_RETRIES));
    if (rc) {
        BNO055_LOG_ERROR(
                   "I2C register write failed at address 0x%02X:0x%02X\n",
                   data_struct.address, reg);
        STATS_INC(g_bno055stats, errors);
        goto err;
    }

    /* Read one byte back */
    payload = 0;
    rc = i2cn_master_read(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                          MYNEWT_VAL(BNO055_I2C_RETRIES));
    *value = payload;
    if (rc) {
        BNO055_LOG_ERROR("Failed to read from 0x%02X:0x%02X\n",
                   data_struct.address, reg);
        STATS_INC(g_bno055stats, errors);
    }

err:
    sensor_itf_unlock(itf);
#endif

    return rc;
}

/**
 * Read data from the sensor of variable length (MAX: 8 bytes)
 *
 * @param The Sensor interface
 * @param Register to read from
 * @param Bufer to read into
 * @param Length of the buffer
 *
 * @return 0 on success and non-zero on failure
 */
static int
bno055_readlen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
               uint8_t len)
{
    int rc;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    rc = bus_node_simple_write_read_transact(itf->si_dev, &reg, 1, buffer, len);
    if (rc) {
        BNO055_LOG_ERROR("Read from bno055 failed %d\n", rc);
        STATS_INC(g_bno055stats, errors);
    }
#else
    uint8_t payload[23] = { reg, 0, 0, 0, 0, 0, 0, 0,
                              0, 0, 0, 0, 0, 0, 0, 0,
                              0, 0, 0, 0, 0, 0, 0};

    struct hal_i2c_master_data data_struct = {
        .address = itf->si_addr,
        .len = 1,
        .buffer = payload
    };

    /* Clear the supplied buffer */
    memset(buffer, 0, len);

    rc = sensor_itf_lock(itf, MYNEWT_VAL(BNO055_ITF_LOCK_TMO));
    if (rc) {
        return rc;
    }

    /* Register write */
    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                           MYNEWT_VAL(BNO055_I2C_RETRIES));
    if (rc) {
        BNO055_LOG_ERROR("I2C access failed at address 0x%02X\n",
                   data_struct.address);
        STATS_INC(g_bno055stats, errors);
        goto err;
    }

    /* Read len bytes back */
    memset(payload, 0, sizeof(payload));
    data_struct.len = len;
    rc = i2cn_master_read(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                          MYNEWT_VAL(BNO055_I2C_RETRIES));
    if (rc) {
        BNO055_LOG_ERROR("Failed to read from 0x%02X:0x%02X\n",
                   data_struct.address, reg);
        STATS_INC(g_bno055stats, errors);
    }

    /* Copy the I2C results into the supplied buffer */
    memcpy(buffer, payload, len);

err:
    sensor_itf_unlock(itf);
#endif

    return rc;
}

/**
 * Setting operation mode for the bno055 sensor
 *
 * @param The sensor interface
 * @param Operation mode for the sensor
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_opr_mode(struct sensor_itf *itf, uint8_t mode)
{
    int rc;

    rc = bno055_write8(itf, BNO055_OPR_MODE_ADDR, BNO055_OPR_MODE_CONFIG);
    if (rc) {
        goto err;
    }

    os_time_delay((OS_TICKS_PER_SEC * 19)/1000 + 1);

    rc = bno055_write8(itf, BNO055_OPR_MODE_ADDR, mode);
    if (rc) {
        goto err;
    }

    /* Refer table 3-6 in the datasheet for the delay values */
    os_time_delay((OS_TICKS_PER_SEC * 7)/1000 + 1);

    return 0;
err:
    return rc;
}

/**
 * Setting power mode for the bno055 sensor
 *
 * @param The sensor interface
 * @param power mode for the sensor
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_pwr_mode(struct sensor_itf *itf, uint8_t mode)
{
    int rc;

    rc = bno055_write8(itf, BNO055_PWR_MODE_ADDR, mode);
    if (rc) {
        goto err;
    }

    os_time_delay((OS_TICKS_PER_SEC * 1)/1000 + 1);

    return 0;
err:
    return rc;
}

/**
 * Read current power mode of the sensor
 *
 * @param The Sensor interface
 * @param ptr to mode variableto fill up
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_pwr_mode(struct sensor_itf *itf, uint8_t *mode)
{
    int rc;
    uint8_t val;

    rc = bno055_read8(itf, BNO055_PWR_MODE_ADDR, &val);
    if (rc) {
        goto err;
    }

    *mode = val;

    return 0;
err:
    return rc;
}

/**
 * Setting units for the bno055 sensor
 *
 * @param The Sensor interface
 * @param power mode for the sensor
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_units(struct sensor_itf *itf, uint8_t val)
{
    int rc;

    rc = bno055_write8(itf, BNO055_UNIT_SEL_ADDR, val);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Get units of the sensor
 *
 * @param The sensor interface
 * @param ptr to the units variable
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_units(struct sensor_itf *itf, uint8_t *units)
{
    int rc;
    uint8_t val;

    rc = bno055_read8(itf, BNO055_UNIT_SEL_ADDR, &val);
    if (rc) {
        goto err;
    }

    *units = val;

    return 0;
err:
    return rc;
}

/**
 * Read current operational mode of the sensor
 *
 * @param The Sensor interface
 * @param ptr to mode variable to fill up
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_opr_mode(struct sensor_itf *itf, uint8_t *mode)
{
    int rc;
    uint8_t val;

    rc = bno055_read8(itf, BNO055_OPR_MODE_ADDR, &val);
    if (rc) {
        goto err;
    }

    *mode = val;

    return 0;
err:
    return rc;
}

static int
bno055_default_cfg(struct bno055_cfg *cfg)
{
    cfg->bc_opr_mode = BNO055_OPR_MODE_ACCONLY;
    cfg->bc_pwr_mode = BNO055_PWR_MODE_NORMAL;
    cfg->bc_units = BNO055_DO_FORMAT_ANDROID|
                    BNO055_ACC_UNIT_MS2|
                    BNO055_ANGRATE_UNIT_DPS|
                    BNO055_EULER_UNIT_DEG|
                    BNO055_TEMP_UNIT_DEGC;
    cfg->bc_placement = BNO055_AXIS_CFG_P1;
    cfg->bc_acc_range = BNO055_ACC_CFG_RNG_4G;
    cfg->bc_acc_bw = BNO055_ACC_CFG_BW_6_25HZ;
    cfg->bc_acc_res = 14;
    cfg->bc_gyro_range = BNO055_GYR_CFG_RNG_2000DPS;
    cfg->bc_gyro_bw = BNO055_GYR_CFG_BW_32HZ;
    cfg->bc_gyro_res = 16;
    cfg->bc_mag_odr = BNO055_MAG_CFG_ODR_2HZ;
    cfg->bc_mag_xy_rep = 15;
    cfg->bc_mag_z_rep = 16;
    cfg->bc_mag_res = BNO055_MAG_RES_13_13_15;
    cfg->bc_mask = SENSOR_TYPE_ACCELEROMETER;

    return 0;
}

/**
 * Expects to be called back through os_dev_create().
 *
 * @param The device object associated with this accelerometer
 * @param Argument passed to OS device init, unused
 *
 * @return 0 on success, non-zero error on failure.
 */
int
bno055_init(struct os_dev *dev, void *arg)
{
    struct bno055 *bno055;
    struct sensor *sensor;
    int rc;

    if (!arg || !dev) {
        rc = SYS_ENODEV;
        goto err;
    }

    bno055 = (struct bno055 *) dev;

    rc = bno055_default_cfg(&bno055->cfg);
    if (rc) {
        goto err;
    }

    sensor = &bno055->sensor;

    /* Initialise the stats entry */
    rc = stats_init(
        STATS_HDR(g_bno055stats),
        STATS_SIZE_INIT_PARMS(g_bno055stats, STATS_SIZE_32),
        STATS_NAME_INIT_PARMS(bno055_stat_section));
    SYSINIT_PANIC_ASSERT(rc == 0);
    /* Register the entry with the stats registry */
    rc = stats_register(dev->od_name, STATS_HDR(g_bno055stats));
    SYSINIT_PANIC_ASSERT(rc == 0);

    rc = sensor_init(sensor, dev);
    if (rc != 0) {
        goto err;
    }

    /* Add the accelerometer/magnetometer driver */
    rc = sensor_set_driver(sensor, SENSOR_TYPE_ACCELEROMETER         |
            SENSOR_TYPE_MAGNETIC_FIELD | SENSOR_TYPE_GYROSCOPE       |
            SENSOR_TYPE_TEMPERATURE    | SENSOR_TYPE_ROTATION_VECTOR |
            SENSOR_TYPE_GRAVITY        | SENSOR_TYPE_LINEAR_ACCEL    |
            SENSOR_TYPE_EULER, (struct sensor_driver *) &g_bno055_sensor_driver);
    if (rc != 0) {
        goto err;
    }

    /* Set the interface */
    rc = sensor_set_interface(sensor, arg);
    if (rc) {
        goto err;
    }

    rc = sensor_mgr_register(sensor);
    if (rc != 0) {
        goto err;
    }

    return (0);
err:
    return (rc);
}

/**
 * Get chip ID from the sensor
 *
 * @param The sensor interface
 * @param Pointer to the variable to fill up chip ID in
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_chip_id(struct sensor_itf *itf, uint8_t *id)
{
    int rc;
    uint8_t idtmp;

    /* Check if we can read the chip address */
    rc = bno055_read8(itf, BNO055_CHIP_ID_ADDR, &idtmp);
    if (rc) {
        goto err;
    }

    *id = idtmp;

    return 0;
err:
    return rc;
}

/**
 * Use external crystal 32.768KHz
 *
 * @param The Sensor interface
 * @param operational mode of the sensor
 * @return 0 on success, non-zero on failure
 */
static int
bno055_set_ext_xtal_use(struct sensor_itf *itf, uint8_t use_xtal, uint8_t mode)
{
    int rc;

    if (mode != BNO055_OPR_MODE_CONFIG) {
        /* Switch to config mode */
        rc = bno055_set_opr_mode(itf, BNO055_OPR_MODE_CONFIG);
        if (rc) {
            goto err;
        }
    }

    os_time_delay((OS_TICKS_PER_SEC * 25)/1000 + 1);

    rc = bno055_write8(itf, BNO055_PAGE_ID_ADDR, 0);
    if (rc) {
        goto err;
    }

    if (use_xtal) {
        /* Use External Clock */
        rc = bno055_write8(itf, BNO055_SYS_TRIGGER_ADDR, BNO055_SYS_TRIGGER_CLK_SEL);
        if (rc) {
            goto err;
        }
    } else {
        /* Use Internal clock */
        rc = bno055_write8(itf, BNO055_SYS_TRIGGER_ADDR, 0x00);
        if (rc) {
            goto err;
        }
    }

    os_time_delay((OS_TICKS_PER_SEC * 10)/1000 + 1);

    /* Reset to previous operating mode */
    rc = bno055_set_opr_mode(itf, mode);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

int
bno055_placement_cfg(struct sensor_itf *itf, uint8_t placement)
{
    uint8_t remap_cfg;
    uint8_t remap_sign;
    int rc;

    rc = SYS_EOK;

    switch(placement) {

    case BNO055_AXIS_CFG_P0:
        remap_cfg = BNO055_REMAP_CONFIG_P0;
        remap_sign = BNO055_REMAP_SIGN_P0;
        break;
    case BNO055_AXIS_CFG_P1:
        remap_cfg = BNO055_REMAP_CONFIG_P1;
        remap_sign = BNO055_REMAP_SIGN_P1;
        break;
    case BNO055_AXIS_CFG_P2:
        remap_cfg = BNO055_REMAP_CONFIG_P2;
        remap_sign = BNO055_REMAP_SIGN_P2;
        break;
    case BNO055_AXIS_CFG_P3:
        remap_cfg = BNO055_REMAP_CONFIG_P3;
        remap_sign = BNO055_REMAP_SIGN_P3;
        break;
    case BNO055_AXIS_CFG_P4:
        remap_cfg = BNO055_REMAP_CONFIG_P4;
        remap_sign = BNO055_REMAP_SIGN_P4;
        break;
    case BNO055_AXIS_CFG_P5:
        remap_cfg = BNO055_REMAP_CONFIG_P5;
        remap_sign = BNO055_REMAP_SIGN_P5;
        break;
    case BNO055_AXIS_CFG_P6:
        remap_cfg = BNO055_REMAP_CONFIG_P6;
        remap_sign = BNO055_REMAP_SIGN_P6;
        break;
    case BNO055_AXIS_CFG_P7:
        remap_cfg = BNO055_REMAP_CONFIG_P7;
        remap_sign = BNO055_REMAP_SIGN_P7;
        break;
    default:
        BNO055_LOG_ERROR("Invalid Axis config, Assuming P1(default) \n");
        rc = SYS_EINVAL;
        goto err;
    }

    rc = bno055_write8(itf, BNO055_AXIS_MAP_CONFIG_ADDR, remap_cfg);
    if (rc) {
        goto err;
    }

    rc = bno055_write8(itf, BNO055_AXIS_MAP_SIGN_ADDR, remap_sign);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

int
bno055_acc_cfg(struct sensor_itf *itf, struct bno055_cfg *cfg)
{
    int rc;

    rc = bno055_write8(itf, BNO055_ACCEL_CONFIG_ADDR, cfg->bc_acc_range|
                       cfg->bc_acc_bw|cfg->bc_acc_opr_mode);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

int
bno055_mag_cfg(struct sensor_itf *itf, struct bno055_cfg *cfg)
{
    int rc;

    rc = bno055_write8(itf, BNO055_MAG_CONFIG_ADDR, cfg->bc_mag_odr|
                       cfg->bc_mag_pwr_mode|cfg->bc_mag_opr_mode);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

int
bno055_gyro_cfg(struct sensor_itf *itf, struct bno055_cfg *cfg)
{
    int rc;

    rc = bno055_write8(itf, BNO055_GYRO_CONFIG_ADDR, cfg->bc_gyro_range|
                       cfg->bc_gyro_bw|cfg->bc_gyro_opr_mode);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

int
bno055_config(struct bno055 *bno055, struct bno055_cfg *cfg)
{
    int rc;
    uint8_t id;
    uint8_t mode;
    struct sensor_itf *itf;

    itf = SENSOR_GET_ITF(&(bno055->sensor));

    /* Check if we can read the chip address */
    rc = bno055_get_chip_id(itf, &id);
    if (rc) {
        goto err;
    }

    if (id != BNO055_ID) {
        os_time_delay((OS_TICKS_PER_SEC * 100)/1000 + 1);

        rc = bno055_get_chip_id(itf, &id);
        if (rc) {
            goto err;
        }

        if(id != BNO055_ID) {
            rc = SYS_EINVAL;
            goto err;
        }
    }

    /* Reset sensor */
    rc = bno055_write8(itf, BNO055_SYS_TRIGGER_ADDR,
                       BNO055_SYS_TRIGGER_RST_SYS);
    if (rc) {
        goto err;
    }

    os_time_delay(OS_TICKS_PER_SEC);

    rc = bno055_set_opr_mode(itf, BNO055_OPR_MODE_CONFIG);
    if (rc) {
        goto err;
    }

    /* Set to normal power mode */
    rc = bno055_set_pwr_mode(itf, cfg->bc_pwr_mode);
    if (rc) {
        goto err;
    }

    bno055->cfg.bc_pwr_mode = cfg->bc_pwr_mode;

    /**
     * As per Section 5.5 in the BNO055 Datasheet,
     * external crystal should be used for accurate
     * results
     */
    rc = bno055_set_ext_xtal_use(itf, cfg->bc_use_ext_xtal, BNO055_OPR_MODE_CONFIG);
    if (rc) {
        goto err;
    }

    bno055->cfg.bc_use_ext_xtal = cfg->bc_use_ext_xtal;

    /* Setting units and data output format */
    rc = bno055_set_units(itf, cfg->bc_units);
    if (rc) {
        goto err;
    }

    bno055->cfg.bc_units = cfg->bc_units;

    /* Change mode to requested mode */
    rc = bno055_set_opr_mode(itf, cfg->bc_opr_mode);
    if (rc) {
        goto err;
    }

    os_time_delay(OS_TICKS_PER_SEC/2);

    rc = bno055_get_opr_mode(itf, &mode);
    if (rc) {
        goto err;
    }

    if (cfg->bc_opr_mode != mode) {

        /* Trying to set operation mode again */
        rc = bno055_set_opr_mode(itf, cfg->bc_opr_mode);
        if (rc) {
            goto err;
        }

        rc = bno055_get_opr_mode(itf, &mode);

        if (rc) {
            goto err;
        }

        if (cfg->bc_opr_mode != mode) {
            BNO055_LOG_ERROR("Config mode and read mode do not match.\n");
            rc = SYS_EINVAL;
            goto err;
        }
    }

    bno055->cfg.bc_opr_mode = cfg->bc_opr_mode;

    rc = bno055_acc_cfg(itf, cfg);
    if (rc) {
        goto err;
    }

    rc = sensor_set_type_mask(&(bno055->sensor), cfg->bc_mask);
    if (rc) {
        goto err;
    }

    bno055->cfg.bc_mask = cfg->bc_mask;

    return 0;
err:
    return rc;
}

/**
 * Get quat data from sensor
 *
 * @param The sensor ineterface
 * @param sensor quat data to be filled up
 * @return 0 on success, non-zero on error
 */
int
bno055_get_quat_data(struct sensor_itf *itf, void *datastruct)
{
    uint8_t buffer[8];
    double scale;
    int rc;
    struct sensor_quat_data *sqd;

    sqd = (struct sensor_quat_data *)datastruct;

    /* As per Section 3.6.5.5 Orientation (Quaternion) */
    scale = (1.0 / (1<<14));

    memset (buffer, 0, 8);

    /* Read quat data */
    rc = bno055_readlen(itf, BNO055_QUATERNION_DATA_W_LSB_ADDR, buffer, 8);

    if (rc) {
        goto err;
    }

    sqd->sqd_w = ((((uint16_t)buffer[1]) << 8) | ((uint16_t)buffer[0])) * scale;
    sqd->sqd_x = ((((uint16_t)buffer[3]) << 8) | ((uint16_t)buffer[2])) * scale;
    sqd->sqd_y = ((((uint16_t)buffer[5]) << 8) | ((uint16_t)buffer[4])) * scale;
    sqd->sqd_z = ((((uint16_t)buffer[7]) << 8) | ((uint16_t)buffer[6])) * scale;

    sqd->sqd_w_is_valid = 1;
    sqd->sqd_x_is_valid = 1;
    sqd->sqd_y_is_valid = 1;
    sqd->sqd_z_is_valid = 1;

    return 0;
err:
    return rc;
}

/**
 * Find register based on sensor type
 *
 * @return register address
 */
static int
bno055_find_reg(sensor_type_t type, uint8_t *reg)
{
    int rc;

    rc = SYS_EOK;
    switch(type) {
        case SENSOR_TYPE_ACCELEROMETER:
            *reg = BNO055_ACCEL_DATA_X_LSB_ADDR;
            break;
        case SENSOR_TYPE_GYROSCOPE:
            *reg = BNO055_GYRO_DATA_X_LSB_ADDR;
            break;
        case SENSOR_TYPE_MAGNETIC_FIELD:
            *reg = BNO055_MAG_DATA_X_LSB_ADDR;
            break;
        case SENSOR_TYPE_EULER:
            *reg = BNO055_EULER_H_LSB_ADDR;
            break;
        case SENSOR_TYPE_LINEAR_ACCEL:
            *reg = BNO055_LINEAR_ACCEL_DATA_X_LSB_ADDR;
            break;
        case SENSOR_TYPE_GRAVITY:
            *reg = BNO055_GRAVITY_DATA_X_LSB_ADDR;
            break;
        default:
            BNO055_LOG_ERROR("Not supported sensor type: %d\n", (int)type);
            rc = SYS_EINVAL;
            break;
    }

    return rc;
}

/**
 * Get vector data from sensor
 *
 * @param The sensor ineterface
 * @param pointer to the structure to be filled up
 * @param Type of sensor
 * @return 0 on success, non-zero on error
 */
int
bno055_get_vector_data(struct sensor_itf *itf, void *datastruct, int type)
{

    uint8_t payload[6];
    int16_t x, y, z;
    struct sensor_mag_data *smd;
    struct sensor_accel_data *sad;
    struct sensor_euler_data *sed;
    uint8_t reg;
    uint8_t units;
    float acc_div;
    float gyro_div;
    float euler_div;

    int rc;

    memset (payload, 0, 6);

    x = y = z = 0;

    rc = bno055_find_reg(type, &reg);
    if (rc) {
        goto err;
    }

    rc = bno055_readlen(itf, reg, payload, 6);
    if (rc) {
        goto err;
    }

    x = ((int16_t)payload[0]) | (((int16_t)payload[1]) << 8);
    y = ((int16_t)payload[2]) | (((int16_t)payload[3]) << 8);
    z = ((int16_t)payload[4]) | (((int16_t)payload[5]) << 8);

    rc = bno055_get_units(itf, &units);
    if (rc) {
        goto err;
    }

    acc_div  = units & BNO055_ACC_UNIT_MG ? 1.0:100.0;
    gyro_div = units & BNO055_ANGRATE_UNIT_RPS ? 900.0:16.0;
    euler_div = units & BNO055_EULER_UNIT_RAD ? 900.0:16.0;

    /**
     * Convert the value to an appropriate range (section 3.6.4)
     */
    switch(type) {
        case SENSOR_TYPE_MAGNETIC_FIELD:
            smd = datastruct;
            /* 1uT = 16 LSB */
            smd->smd_x = ((double)x)/16.0;
            smd->smd_y = ((double)y)/16.0;
            smd->smd_z = ((double)z)/16.0;

            smd->smd_x_is_valid = 1;
            smd->smd_y_is_valid = 1;
            smd->smd_z_is_valid = 1;

            break;
        case SENSOR_TYPE_GYROSCOPE:
            sad = datastruct;
            /* 1rps = 900 LSB */
            sad->sad_x = ((double)x)/gyro_div;
            sad->sad_y = ((double)y)/gyro_div;
            sad->sad_z = ((double)z)/gyro_div;

            sad->sad_x_is_valid = 1;
            sad->sad_y_is_valid = 1;
            sad->sad_z_is_valid = 1;
            break;
        case SENSOR_TYPE_EULER:
            sed = datastruct;
            /* 1 degree = 16 LSB */
            sed->sed_h = ((double)x)/euler_div;
            sed->sed_r = ((double)y)/euler_div;
            sed->sed_p = ((double)z)/euler_div;

            sed->sed_h_is_valid = 1;
            sed->sed_r_is_valid = 1;
            sed->sed_p_is_valid = 1;
            break;
        case SENSOR_TYPE_ACCELEROMETER:
        case SENSOR_TYPE_LINEAR_ACCEL:
        case SENSOR_TYPE_GRAVITY:
            sad = datastruct;
            /* 1m/s^2 = 100 LSB */
            sad->sad_x = ((double)x)/acc_div;
            sad->sad_y = ((double)y)/acc_div;
            sad->sad_z = ((double)z)/acc_div;

            sad->sad_x_is_valid = 1;
            sad->sad_y_is_valid = 1;
            sad->sad_z_is_valid = 1;
            break;
        default:
            BNO055_LOG_ERROR("Not supported sensor type: %d\n", type);
            rc = SYS_EINVAL;
            goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Get temperature from bno055 sensor
 *
 * @param The Sensor interface
 * @param pointer to the temperature variable to be filled up
 * @return 0 on success, non-zero on error
 */
int
bno055_get_temp(struct sensor_itf *itf, uint8_t *temp)
{
    int rc;
    uint8_t units;
    uint8_t div;

    rc = bno055_read8(itf, BNO055_TEMP_ADDR, temp);
    if (rc) {
        goto err;
    }

    rc = bno055_get_units(itf, &units);
    if (rc) {
        goto err;
    }

    div = units & BNO055_TEMP_UNIT_DEGF ? 2 : 1;

    *temp = *temp/div;

    return 0;
err:
    return rc;
}

/**
 * Get temperature data from bno055 sensor and mark it valid
 *
 * @param The sensor interface
 * @param pointer to the temperature data structure
 * @return 0 on success, non-zero on error
 */
static int
bno055_get_temp_data(struct sensor_itf *itf, struct sensor_temp_data *std)
{
    int rc;
    uint8_t temp;

    rc = bno055_get_temp(itf, &temp);
    if (rc) {
        goto err;
    }

    std->std_temp = temp;
    std->std_temp_is_valid = 1;

    return 0;
err:
    return rc;
}

/**
 * Get sensor data of specific type. This function also allocates a buffer
 * to fill up the data in.
 *
 * @param Sensor structure used by the SensorAPI
 * @param Sensor type
 * @param Data function provided by the caler of the API
 * @param Argument for the data function
 * @param Timeout if any for reading
 * @return 0 on success, non-zero on error
 */
static int
bno055_sensor_read(struct sensor *sensor, sensor_type_t type,
        sensor_data_func_t data_func, void *data_arg, uint32_t timeout)
{
    int rc;
    struct sensor_itf *itf;

    union {
        struct sensor_quat_data sqd;
        struct sensor_euler_data sed;
        struct sensor_accel_data sad;
        struct sensor_accel_data slad;
        struct sensor_accel_data sgrd;
        struct sensor_mag_data smd;
        struct sensor_gyro_data sgd;
        struct sensor_temp_data std;
    } databuf;


    itf = SENSOR_GET_ITF(sensor);

    if (type & SENSOR_TYPE_ROTATION_VECTOR) {
        /* Quaternion is a rotation vector */
        rc = bno055_get_quat_data(itf, &databuf.sqd);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf, SENSOR_TYPE_ROTATION_VECTOR);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_TEMPERATURE) {
        rc = bno055_get_temp_data(itf, &databuf.std);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.std, SENSOR_TYPE_TEMPERATURE);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_EULER) {
        /* Get vector data, accel or gravity values */
        rc = bno055_get_vector_data(itf, &databuf.sed, SENSOR_TYPE_EULER);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.sed, SENSOR_TYPE_EULER);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_ACCELEROMETER) {
        /* Get vector data, accel or gravity values */
        rc = bno055_get_vector_data(itf, &databuf.sad, SENSOR_TYPE_ACCELEROMETER);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.sad, SENSOR_TYPE_ACCELEROMETER);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_LINEAR_ACCEL) {
        /* Get vector data, accel or gravity values */
        rc = bno055_get_vector_data(itf, &databuf.slad, SENSOR_TYPE_LINEAR_ACCEL);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.slad, SENSOR_TYPE_LINEAR_ACCEL);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_MAGNETIC_FIELD) {
        /* Get vector data, accel or gravity values */
        rc = bno055_get_vector_data(itf, &databuf.smd, SENSOR_TYPE_MAGNETIC_FIELD);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.smd, SENSOR_TYPE_MAGNETIC_FIELD);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_GYROSCOPE) {
        /* Get vector data, accel or gravity values */
        rc = bno055_get_vector_data(itf, &databuf.sgd, SENSOR_TYPE_GYROSCOPE);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.sgd, SENSOR_TYPE_GYROSCOPE);
        if (rc) {
            goto err;
        }
    }

    if (type & SENSOR_TYPE_GRAVITY) {
        /* Get vector data, accel or gravity values */
        rc = bno055_get_vector_data(itf, &databuf.sgrd, SENSOR_TYPE_GRAVITY);
        if (rc) {
            goto err;
        }

        /* Call data function */
        rc = data_func(sensor, data_arg, &databuf.sgrd, SENSOR_TYPE_GRAVITY);
        if (rc) {
            goto err;
        }
    }

    return 0;
err:
    return rc;
}

/**
 * Gets system status, test results and errors if any from the sensor
 *
 * @param The sensor interface
 * @param ptr to system status
 * @param ptr to self test result
 * @param ptr to system error
 */
int
bno055_get_sys_status(struct sensor_itf *itf, uint8_t *system_status,
                      uint8_t *self_test_result, uint8_t *system_error)
{
    int rc;

    rc = bno055_write8(itf, BNO055_PAGE_ID_ADDR, 0);
    if (rc) {
        goto err;
    }
    /**
     * System Status (see section 4.3.58)
     * ---------------------------------
     * bit 0: Idle
     * bit 1: System Error
     * bit 2: Initializing Peripherals
     * bit 3: System Iniitalization
     * bit 4: Executing Self-Test
     * bit 5: Sensor fusion algorithm running
     * bit 6: System running without fusion algorithms
     */

    if (system_status != 0) {
        rc = bno055_read8(itf, BNO055_SYS_STAT_ADDR, system_status);
        if (rc) {
            goto err;
        }
    }

    /**
     * Self Test Results (see section )
     * --------------------------------
     * 1: test passed, 0: test failed
     * bit 0: Accelerometer self test
     * bit 1: Magnetometer self test
     * bit 2: Gyroscope self test
     * bit 3: MCU self test
     *
     * 0x0F : All Good
     */

    if (self_test_result != 0) {
        rc = bno055_read8(itf, BNO055_SELFTEST_RESULT_ADDR, self_test_result);
        if (rc) {
            goto err;
        }
    }

    /**
     * System Error (see section 4.3.59)
     * ---------------------------------
     * bit 0  : No error
     * bit 1  : Peripheral initialization error
     * bit 2  : System initialization error
     * bit 3  : Self test result failed
     * bit 4  : Register map value out of range
     * bit 5  : Register map address out of range
     * bit 6  : Register map write error
     * bit 7  : BNO low power mode not available for selected operat ion mode
     * bit 8  : Accelerometer power mode not available
     * bit 9  : Fusion algorithm configuration error
     * bit 10 : Sensor configuration error
     */

    if (system_error != 0) {
        rc = bno055_read8(itf, BNO055_SYS_ERR_ADDR, system_error);
        if (rc) {
            goto err;
        }
    }

    os_time_delay((OS_TICKS_PER_SEC * 200)/1000 + 1);

    return 0;
err:
    return rc;
}

/**
 * Get Revision info for different sensors in the bno055
 *
 * @param The sensor interface
 * @param pass the pointer to the revision structure
 * @return 0 on success, non-zero on error
 */
int
bno055_get_rev_info(struct sensor_itf *itf, struct bno055_rev_info *ri)
{
    uint8_t sw_rev_l, sw_rev_h;
    int rc;

    memset(ri, 0, sizeof(struct bno055_rev_info));

    /* Check the accelerometer revision */
    rc = bno055_read8(itf, BNO055_ACCEL_REV_ID_ADDR, &(ri->bri_accel_rev));
    if (rc) {
        goto err;
    }

    /* Check the magnetometer revision */
    rc = bno055_read8(itf, BNO055_MAG_REV_ID_ADDR, &(ri->bri_mag_rev));
    if (rc) {
        goto err;
    }

    /* Check the gyroscope revision */
    rc = bno055_read8(itf, BNO055_GYRO_REV_ID_ADDR, &(ri->bri_gyro_rev));
    if (rc) {
        goto err;
    }

    rc = bno055_read8(itf, BNO055_BL_REV_ID_ADDR, &(ri->bri_bl_rev));
    if (rc) {
        goto err;
    }

    /* Check the SW revision */
    rc = bno055_read8(itf, BNO055_SW_REV_ID_LSB_ADDR, &sw_rev_l);
    if (rc) {
        goto err;
    }

    rc = bno055_read8(itf, BNO055_SW_REV_ID_MSB_ADDR, &sw_rev_h);
    if (rc) {
        goto err;
    }

    ri->bri_sw_rev = (((uint16_t)sw_rev_h) << 8) | ((uint16_t)sw_rev_l);

    return 0;
err:
    return rc;
}

/**
 * Gets current calibration status
 *
 * @param The sensor interface
 * @param Calibration info structure to fill up calib state
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_calib_status(struct sensor_itf *itf, struct bno055_calib_info *bci)
{
    uint8_t status;
    int rc;

    rc = bno055_read8(itf, BNO055_CALIB_STAT_ADDR, &status);
    if (rc) {
        goto err;
    }

    bci->bci_sys = (status >> 6) & 0x03;
    bci->bci_gyro = (status >> 4) & 0x03;
    bci->bci_accel = (status >> 2) & 0x03;
    bci->bci_mag = status & 0x03;

    return 0;
err:
    return rc;
}

/**
 * Checks if bno055 is fully calibrated
 *
 * @param The sensor interface
 * @return 0 on success, non-zero on failure
 */
int
bno055_is_calib(struct sensor_itf *itf)
{
    struct bno055_calib_info bci;
    int rc;

    rc = bno055_get_calib_status(itf, &bci);
    if (rc) {
        goto err;
    }

    if (bci.bci_sys< 3 || bci.bci_gyro < 3 || bci.bci_accel < 3 || bci.bci_mag < 3) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Reads the sensor's offset registers into a byte array
 *
 * @param The sensor interface
 * @param byte array to return offsets into
 * @return 0 on success, non-zero on failure
 *
 */
int
bno055_get_raw_sensor_offsets(struct sensor_itf *itf, uint8_t *offsets)
{
    uint8_t prev_mode;
    int rc;

    rc = SYS_EOK;
    if (!bno055_is_calib(itf)) {
        rc = bno055_get_opr_mode(itf, &prev_mode);
        if (rc) {
            goto err;
        }

        rc = bno055_set_opr_mode(itf, BNO055_OPR_MODE_CONFIG);
        if (rc) {
            goto err;
        }

        rc = bno055_readlen(itf, BNO055_ACCEL_OFFSET_X_LSB_ADDR, offsets,
                            BNO055_NUM_OFFSET_REGISTERS);
        if (rc) {
            goto err;
        }

        rc = bno055_set_opr_mode(itf, prev_mode);
        if (rc) {
            goto err;
        }

        return 0;
    }
err:
    return rc;
}

/**
 *
 * Reads the sensor's offset registers into an offset struct
 *
 * @param The sensor interface
 * @param structure to fill up offsets data
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_sensor_offsets(struct sensor_itf *itf,
                          struct bno055_sensor_offsets *offsets)
{
    uint8_t payload[22];
    int rc;


    rc = bno055_get_raw_sensor_offsets(itf, payload);
    if (rc) {
        goto err;
    }

    offsets->bso_acc_off_x  = (payload[1] << 8)  | payload[0];
    offsets->bso_acc_off_y  = (payload[3] << 8)  | payload[2];
    offsets->bso_acc_off_z  = (payload[5] << 8)  | payload[4];

    offsets->bso_gyro_off_x = (payload[7] << 8)  | payload[6];
    offsets->bso_gyro_off_y = (payload[9] << 8)  | payload[8];
    offsets->bso_gyro_off_z = (payload[11] << 8) | payload[10];

    offsets->bso_mag_off_x  = (payload[13] << 8) | payload[12];
    offsets->bso_mag_off_y  = (payload[15] << 8) | payload[14];
    offsets->bso_mag_off_z  = (payload[17] << 8) | payload[16];

    offsets->bso_acc_radius = (payload[19] << 8) | payload[18];
    offsets->bso_mag_radius = (payload[21] << 8) | payload[20];

    return 0;
err:
    return rc;
}


/**
 *
 * Writes calibration data to the sensor's offset registers
 *
 * @param The sensor interface
 * @param calibration data
 * @param calibration data length
 * @return 0 on success, non-zero on success
 */
int
bno055_set_sensor_raw_offsets(struct sensor_itf *itf, uint8_t* calibdata,
                              uint8_t len)
{
    uint8_t prev_mode;
    int rc;

    if (len != 22) {
        rc = SYS_EINVAL;
        goto err;
    }

    rc = bno055_get_opr_mode(itf, &prev_mode);
    if (rc) {
        goto err;
    }

    rc = bno055_set_opr_mode(itf, BNO055_OPR_MODE_CONFIG);
    if (rc) {
        goto err;
    }

    os_time_delay((25 * OS_TICKS_PER_SEC)/1000 + 1);

    rc = bno055_writelen(itf, BNO055_ACCEL_OFFSET_X_LSB_ADDR, calibdata, len);
    if (rc) {
        goto err;
    }

    rc = bno055_set_opr_mode(itf, prev_mode);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 *
 * Writes to the sensor's offset registers from an offset struct
 *
 * @param The sensor interface
 * @param pointer to the offset structure
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_sensor_offsets(struct sensor_itf *itf,
                          struct bno055_sensor_offsets  *offsets)
{
    uint8_t prev_mode;
    int rc;

    rc = bno055_get_opr_mode(itf, &prev_mode);
    if (rc) {
        goto err;
    }

    rc = bno055_set_opr_mode(itf, BNO055_OPR_MODE_CONFIG);
    if (rc) {
        goto err;
    }

    os_time_delay((25 * OS_TICKS_PER_SEC)/1000 + 1);

    rc |= bno055_write8(itf, BNO055_ACCEL_OFFSET_X_LSB_ADDR, (offsets->bso_acc_off_x) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_ACCEL_OFFSET_X_MSB_ADDR, (offsets->bso_acc_off_x >> 8) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_ACCEL_OFFSET_Y_LSB_ADDR, (offsets->bso_acc_off_y) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_ACCEL_OFFSET_Y_MSB_ADDR, (offsets->bso_acc_off_y >> 8) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_ACCEL_OFFSET_Z_LSB_ADDR, (offsets->bso_acc_off_z) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_ACCEL_OFFSET_Z_MSB_ADDR, (offsets->bso_acc_off_z >> 8) & 0x0FF);

    rc |= bno055_write8(itf, BNO055_GYRO_OFFSET_X_LSB_ADDR, (offsets->bso_gyro_off_x) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_GYRO_OFFSET_X_MSB_ADDR, (offsets->bso_gyro_off_x >> 8) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_GYRO_OFFSET_Y_LSB_ADDR, (offsets->bso_gyro_off_y) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_GYRO_OFFSET_Y_MSB_ADDR, (offsets->bso_gyro_off_y >> 8) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_GYRO_OFFSET_Z_LSB_ADDR, (offsets->bso_gyro_off_z) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_GYRO_OFFSET_Z_MSB_ADDR, (offsets->bso_gyro_off_z >> 8) & 0x0FF);

    rc |= bno055_write8(itf, BNO055_MAG_OFFSET_X_LSB_ADDR, (offsets->bso_mag_off_x) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_MAG_OFFSET_X_MSB_ADDR, (offsets->bso_mag_off_x >> 8) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_MAG_OFFSET_Y_LSB_ADDR, (offsets->bso_mag_off_y) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_MAG_OFFSET_Y_MSB_ADDR, (offsets->bso_mag_off_y >> 8) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_MAG_OFFSET_Z_LSB_ADDR, (offsets->bso_mag_off_z) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_MAG_OFFSET_Z_MSB_ADDR, (offsets->bso_mag_off_z >> 8) & 0x0FF);

    rc |= bno055_write8(itf, BNO055_ACCEL_RADIUS_LSB_ADDR, (offsets->bso_acc_radius) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_ACCEL_RADIUS_MSB_ADDR, (offsets->bso_acc_radius >> 8) & 0x0FF);

    rc |= bno055_write8(itf, BNO055_MAG_RADIUS_LSB_ADDR, (offsets->bso_mag_radius) & 0x0FF);
    rc |= bno055_write8(itf, BNO055_MAG_RADIUS_MSB_ADDR, (offsets->bso_mag_radius >> 8) & 0x0FF);

    rc |= bno055_set_opr_mode(itf, prev_mode);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Get threshold for interrupts
 *
 * @param The sensor interface
 * @param ptr to threshold
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_int_thresh(struct sensor_itf *itf, uint32_t intr, uint8_t *thresh)
{
    int rc;
    uint8_t val;
    uint8_t mask;
    uint8_t reg;

    mask = 0;
    switch(intr) {
        case BNO055_INT_ACC_HG:
            reg = BNO055_ACCEL_HIGH_G_THRES_ADDR;
            break;
        case BNO055_INT_ACC_SM:
        case BNO055_INT_ACC_NM:
            reg = BNO055_ACCEL_NO_MOTION_THRES_ADDR;
            break;
        case BNO055_INT_ACC_AM:
            reg = BNO055_ACCEL_ANY_MOTION_THRES_ADDR;
            break;
        case BNO055_INT_GYR_AM:
            reg = BNO055_GYRO_ANY_MOTION_THRES_ADDR;
            mask = 0x3F;
            break;
        case BNO055_INT_GYR_HR_X_AXIS:
            reg = BNO055_GYRO_HIGHRATE_X_SET_ADDR;
            mask = 0x1F;
            break;
        case BNO055_INT_GYR_HR_Y_AXIS:
            reg = BNO055_GYRO_HIGHRATE_Y_SET_ADDR;
            mask = 0x1F;
            break;
        case BNO055_INT_GYR_HR_Z_AXIS:
            reg = BNO055_GYRO_HIGHRATE_Z_SET_ADDR;
            mask = 0x1F;
            break;
        default:
            rc = SYS_EINVAL;
            goto err;
    }

    rc = bno055_read8(itf, reg, &val);
    if (rc) {
        goto err;
    }

    *thresh = val | mask;

    return 0;
err:
    return rc;
}

/**
 * Set threshold for interrupts
 *
 * @param The sensor interface
 * @param threshold
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_int_thresh(struct sensor_itf *itf, uint32_t intr, uint8_t thresh)
{
    int rc;
    uint8_t mask;
    uint8_t val;
    uint8_t reg;

    mask = val = 0;
    switch(intr) {
        case BNO055_INT_ACC_HG:
            reg = BNO055_ACCEL_HIGH_G_THRES_ADDR;
            break;
        case BNO055_INT_ACC_SM:
        case BNO055_INT_ACC_NM:
            reg = BNO055_ACCEL_NO_MOTION_THRES_ADDR;
            break;
        case BNO055_INT_ACC_AM:
            reg = BNO055_ACCEL_ANY_MOTION_THRES_ADDR;
            break;
        case BNO055_INT_GYR_AM:
            reg = BNO055_GYRO_ANY_MOTION_THRES_ADDR;
            mask = 0x3F;
            break;
        case BNO055_INT_GYR_HR_X_AXIS:
            reg = BNO055_GYRO_HIGHRATE_X_SET_ADDR;
            mask = 0x1F;
            break;
        case BNO055_INT_GYR_HR_Y_AXIS:
            reg = BNO055_GYRO_HIGHRATE_Y_SET_ADDR;
            mask = 0x1F;
            break;
        case BNO055_INT_GYR_HR_Z_AXIS:
            reg = BNO055_GYRO_HIGHRATE_Z_SET_ADDR;
            mask = 0x1F;
            break;
        default:
            rc = SYS_EINVAL;
            goto err;
    }

    if (mask && thresh > mask) {
        rc = SYS_EINVAL;
        goto err;
    }

    if (mask) {
        rc = bno055_read8(itf, reg, &val);
        if (rc) {
            goto err;
        }
    }

    thresh = thresh | val;

    rc = bno055_write8(itf, reg, thresh);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Get interrupt trigger delay
 *
 * @param The sensor interface
 * @param ptr to duration
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_int_duration(struct sensor_itf *itf, uint32_t intr, uint8_t *duration)
{
    int rc;
    uint8_t val;
    uint8_t mask;
    uint8_t shift;
    uint8_t reg;

    mask = val = shift = 0;
    switch(intr) {
        case BNO055_INT_GYR_HR_X_AXIS:
            reg = BNO055_GYRO_DURN_X_ADDR;
            break;
        case BNO055_INT_GYR_HR_Y_AXIS:
            reg = BNO055_GYRO_DURN_Y_ADDR;
            break;
        case BNO055_INT_GYR_HR_Z_AXIS:
            reg = BNO055_GYRO_DURN_Z_ADDR;
            break;
        case BNO055_INT_ACC_HG:
            reg = BNO055_ACCEL_HIGH_G_DURN_ADDR;
            break;
        case BNO055_INT_ACC_NM:
            reg = BNO055_ACCEL_NO_MOTION_SET_ADDR;
            mask = 0x3F;
            shift = 1;
            break;
        case BNO055_INT_ACC_AM:
            reg = BNO055_ACCEL_INTR_SETTINGS_ADDR;
            mask = 0x3;
            break;
        case BNO055_INT_GYR_AM:
            reg = BNO055_GYRO_INTR_SETTINGS_ADDR;
            mask = 0xc;
            shift = 2;
            break;
        default:
            rc = SYS_EINVAL;
            goto err;
    }

    rc = bno055_read8(itf, reg, &val);
    if (rc) {
        goto err;
    }

    *duration = val | mask;

    if (shift) {
        *duration >>= shift;
    }

    return 0;
err:
    return rc;
}

/**
 * Set interrupt trigger delay
 *
 * @param The sensor interface 
 * @param ptr to duration
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_int_duration(struct sensor_itf *itf, uint32_t intr, uint8_t duration)
{
    int rc;
    uint8_t val;
    uint8_t mask;
    uint8_t shift;
    uint8_t reg;

    val = mask = shift = 0;
    switch(intr) {
        case BNO055_INT_GYR_HR_X_AXIS:
            reg = BNO055_GYRO_DURN_X_ADDR;
            break;
        case BNO055_INT_GYR_HR_Y_AXIS:
            reg = BNO055_GYRO_DURN_Y_ADDR;
            break;
        case BNO055_INT_GYR_HR_Z_AXIS:
            reg = BNO055_GYRO_DURN_Z_ADDR;
            break;
        case BNO055_INT_ACC_HG:
            reg = BNO055_ACCEL_HIGH_G_DURN_ADDR;
            break;
        case BNO055_INT_ACC_NM:
            reg = BNO055_ACCEL_NO_MOTION_SET_ADDR;
            mask = 0x3F;
            shift = 1;
            break;
        case BNO055_INT_ACC_AM:
            reg = BNO055_ACCEL_INTR_SETTINGS_ADDR;
            mask = 0x3;
            break;
        case BNO055_INT_GYR_AM:
            reg = BNO055_GYRO_INTR_SETTINGS_ADDR;
            mask = 0x3;
            shift = 2;
            break;
        default:
            rc = SYS_EINVAL;
            goto err;
    }

    if (mask && duration > mask) {
        rc = SYS_EINVAL;
        goto err;
    }

    rc = bno055_read8(itf, reg, &val);
    if (rc) {
        goto err;
    }

    if (shift) {
        duration <<= shift;
    }

    if (mask) {
        rc = bno055_read8(itf, reg, &val);
        if (rc) {
            goto err;
        }
    }

    val |= duration;

    rc = bno055_write8(itf, reg, duration);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Enable axis interrupt,
 *
 * @param The sensor interface
 * @param interrupt axis
 * @return 0 on success, non-zero on failure
 */
int
bno055_enable_int_axis(struct sensor_itf *itf, uint32_t intr_axis, uint8_t enable)
{
    int rc;
    uint8_t reg;
    uint8_t val;
    uint8_t intr;

    intr = 0;
    if (intr_axis & BNO055_INT_ACC_AM || intr_axis & BNO055_INT_ACC_NM) {
        reg = BNO055_ACCEL_INTR_SETTINGS_ADDR;
        intr = (intr_axis >> BNO055_INT_ACC_AM_POS) & 0xFF;
    }

    if (intr_axis & BNO055_INT_ACC_HG) {
        reg = BNO055_ACCEL_INTR_SETTINGS_ADDR;
        intr = (intr_axis >> BNO055_INT_ACC_HG_POS) & 0xFF;
    }

    rc = bno055_read8(itf, reg, &val);
    if (rc) {
        goto err;
    }

    intr = enable ? intr | val : intr & val;

    rc = bno055_write8(itf, reg, intr);
    if (rc) {
        goto err;
    }

    if (intr_axis & BNO055_INT_GYR_AM) {
        reg = BNO055_GYRO_INTR_SETTINGS_ADDR;
        intr = (intr_axis >> BNO055_INT_GYR_AM_POS) & 0xFF;
    }

    if (intr_axis & BNO055_INT_GYR_HR) {
        reg = BNO055_GYRO_INTR_SETTINGS_ADDR;
        intr = (intr_axis >> BNO055_INT_GYR_HR_POS) & 0xFF;
    }

    rc = bno055_read8(itf, reg, &val);
    if (rc) {
        goto err;
    }

    intr = enable ? intr | val : intr & val;

    rc = bno055_write8(itf, reg, intr);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Get acc int settings
 *
 * @param The sensor interface
 * @param ptr to int settings
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_acc_int_settings(struct sensor_itf *itf, uint8_t *settings)
{
    int rc;
    uint8_t val;

    rc = bno055_read8(itf, BNO055_ACCEL_INTR_SETTINGS_ADDR, &val);
    if (rc) {
        goto err;
    }

    *settings = val;
err:
    return rc;
}

/**
 * Set acc int settings
 *
 * @param The sensor interface
 * @param int settings
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_acc_int_settings(struct sensor_itf *itf, uint8_t settings)
{
    int rc;

    rc = bno055_write8(itf, BNO055_ACCEL_INTR_SETTINGS_ADDR, settings);
    if (rc) {
        goto err;
    }

err:
    return rc;
}

/**
 * Get enabled/disabled interrupts
 *
 * @param The sensor interface
 * @param ptr to interrupt mask
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_int_enable(struct sensor_itf *itf, uint8_t *intr)
{
    int rc;
    uint8_t val;
    uint8_t mask;

    rc = bno055_read8(itf, BNO055_INT_EN_ADDR, &val);
    if (rc) {
        goto err;
    }

    mask = (val & BNO055_INT_EN_ACC_AM ? BNO055_INT_ACC_AM : 0);
    mask |= (val & BNO055_INT_EN_ACC_HG ? BNO055_INT_ACC_HG : 0);
    mask |= (val & BNO055_INT_EN_GYR_HR ? BNO055_INT_GYR_HR : 0);
    mask |= (val & BNO055_INT_EN_GYR_AM ? BNO055_INT_GYR_AM : 0);

    if (val & BNO055_INT_EN_ACC_NM) {
        val = 0;
        rc = bno055_read8(itf, BNO055_ACCEL_NO_MOTION_SET_ADDR, &val);
        if (rc) {
            goto err;
        }

        mask |= (val & BNO055_ACCEL_SMNM ? BNO055_INT_ACC_SM : BNO055_INT_ACC_NM);
    }

    *intr = mask;

    return 0;
err:
    return rc;
}

/**
 * Enable/Disable interrupts
 *
 * @param The sensor interface
 * @param Interrupt mask
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_int_enable(struct sensor_itf *itf, uint8_t intr, uint8_t enable)
{
    int rc;
    uint8_t mask;
    uint8_t smnm;
    uint8_t val;

    mask  = (intr & BNO055_INT_ACC_AM ? BNO055_INT_EN_ACC_AM : 0);
    mask |= (intr & BNO055_INT_ACC_HG ? BNO055_INT_EN_ACC_HG : 0);
    mask |= (intr & BNO055_INT_GYR_HR ? BNO055_INT_EN_GYR_HR : 0);
    mask |= (intr & BNO055_INT_GYR_AM ? BNO055_INT_EN_GYR_AM : 0);

    /* Both of them can't be set */
    if (intr & BNO055_INT_ACC_NM && intr & BNO055_INT_ACC_SM) {
        rc = SYS_EINVAL;
        goto err;
    }

    smnm = 0;
    if (intr & BNO055_INT_ACC_SM) {
        smnm = 0xF0 | BNO055_ACCEL_SMNM;
        mask |= BNO055_INT_EN_ACC_NM;
    } else if (intr & BNO055_INT_ACC_NM) {
        smnm = 0xF0;
        mask |= BNO055_INT_EN_ACC_NM;
    }

    if (smnm) {
        smnm &= 0x0F;
        rc = bno055_read8(itf, BNO055_ACCEL_NO_MOTION_SET_ADDR, &val);
        if (rc) {
            goto err;
        }

        val |= smnm;
        rc = bno055_write8(itf, BNO055_ACCEL_NO_MOTION_SET_ADDR, val);
        if (rc) {
            goto err;
        }
    }

    val = 0;
    rc = bno055_read8(itf, BNO055_INT_EN_ADDR, &val);
    if (rc) {
        goto err;
    }

    if (enable) {
        val |= mask;
    } else {
        val &= ~mask;
    }

    rc = bno055_write8(itf, BNO055_INT_EN_ADDR, val);
    if (rc) {
        goto err;
    }

    return 0;
err:
    return rc;
}

/**
 * Get interrupt status
 *
 * @param The sensor interface
 * @param ptr to interrupt status to fill up
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_int_status(struct sensor_itf *itf, uint8_t *int_mask)
{
    int rc;
    uint8_t val;

    rc = bno055_read8(itf, BNO055_INTR_STAT_ADDR, &val);
    if (rc) {
        goto err;
    }

    *int_mask = val;
err:
    return rc;
}

/**
 * Set interrupt mask
 *
 * @param The sensor interface
 * @param Interrupt mask
 * @return 0 on success, non-zero on failure
 */
int
bno055_set_int_mask(struct sensor_itf *itf, uint8_t int_mask)
{
    int rc;

    rc = bno055_write8(itf, BNO055_INT_MASK_ADDR, int_mask);
    if (rc) {
        goto err;
    }

err:
    return rc;
}

/**
 * Get interrupt mask
 *
 * @param The sensor interface
 * @param ptr to interrupt mask
 * @return 0 on success, non-zero on failure
 */
int
bno055_get_int_mask(struct sensor_itf *itf, uint8_t *int_mask)
{
    int rc;
    uint8_t val;

    rc = bno055_read8(itf, BNO055_INT_MASK_ADDR, &val);
    if (rc) {
        goto err;
    }

    *int_mask = val;
err:
    return rc;
}

static int
bno055_sensor_get_config(struct sensor *sensor, sensor_type_t type,
        struct sensor_cfg *cfg)
{
    int rc;

    if ((type != SENSOR_TYPE_ACCELEROMETER)   &&
        (type != SENSOR_TYPE_MAGNETIC_FIELD)  &&
        (type != SENSOR_TYPE_TEMPERATURE)     &&
        (type != SENSOR_TYPE_ROTATION_VECTOR) &&
        (type != SENSOR_TYPE_LINEAR_ACCEL)    &&
        (type != SENSOR_TYPE_GRAVITY)         &&
        (type != SENSOR_TYPE_EULER)) {
        rc = SYS_EINVAL;
        goto err;
    }

    if (type != SENSOR_TYPE_TEMPERATURE) {
        cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT_TRIPLET;
    } else {
        cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT;
    }

    return 0;
err:
    return rc;
}

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)

void
init_node_cb(struct bus_node *node, void *arg)
{
    bno055_init((struct os_dev *)node, arg);
}

int
bno055_create_sensor_dev(struct bno055 *dev, const char *name,
                         const struct bus_i2c_node_cfg *i2c_cfg,
                         struct sensor_itf *sensor_itf)
{
    struct bus_node_callbacks cbs = {
        .init = init_node_cb,
    };
    int rc;

    sensor_itf->si_dev = &dev->i2c_node.bnode.odev;
    bus_node_set_callbacks((struct os_dev *)dev, &cbs);

    rc = bus_i2c_node_create(name, &dev->i2c_node, i2c_cfg, sensor_itf);

    return rc;
}
#endif
