/*
 * 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 <math.h>

#include "os/mynewt.h"
#include "hal/hal_i2c.h"
#include "hal/hal_spi.h"
#include "hal/hal_gpio.h"
#include "i2cn/i2cn.h"
#include "sensor/sensor.h"
#include "sensor/accel.h"
#include "adxl345/adxl345.h"
#include "adxl345_priv.h"
#include "modlog/modlog.h"
#include "stats/stats.h"
#include <syscfg/syscfg.h>

#if !MYNEWT_VAL(BUS_DRIVER_PRESENT)
static struct hal_spi_settings spi_adxl345_settings = {
    .data_order = HAL_SPI_MSB_FIRST,
    .data_mode  = HAL_SPI_MODE3,
    .baudrate   = 4000,
    .word_size  = HAL_SPI_WORD_SIZE_8BIT,
};
#endif

/* Define the stats section and records */
STATS_SECT_START(adxl345_stat_section)
    STATS_SECT_ENTRY(read_errors)
    STATS_SECT_ENTRY(write_errors)
STATS_SECT_END

/* Define stat names for querying */
STATS_NAME_START(adxl345_stat_section)
    STATS_NAME(adxl345_stat_section, read_errors)
    STATS_NAME(adxl345_stat_section, write_errors)
STATS_NAME_END(adxl345_stat_section)

/* Global variable used to hold stats data */
STATS_SECT_DECL(adxl345_stat_section) g_adxl345stats;

#define ADXL345_NOTIFY_MASK  0x01
#define ADXL345_READ_MASK    0x02

#if MYNEWT_VAL(ADXL345_INT_ENABLE)
static void interrupt_handler(void * arg);
static int init_intpin(struct adxl345 * adxl345, hal_gpio_irq_handler_t handler,
                       void * arg);
static int enable_interrupt(struct sensor * sensor, uint8_t ints_to_enable);
static int disable_interrupt(struct sensor * sensor, uint8_t ints_to_disable);
#endif

/* Exports for the sensor API */
static int adxl345_sensor_read(struct sensor *, sensor_type_t,
                               sensor_data_func_t, void *, uint32_t);
static int adxl345_sensor_get_config(struct sensor *, sensor_type_t,
                                     struct sensor_cfg *);
static int adxl345_sensor_set_trigger_thresh(struct sensor * sensor,
                                             sensor_type_t sensor_type,
                                             struct sensor_type_traits * stt);
static int adxl345_sensor_set_notification(struct sensor * sensor,
                                           sensor_event_type_t sensor_event_type);
static int adxl345_sensor_unset_notification(struct sensor * sensor,
                                             sensor_event_type_t sensor_event_type);
static int adxl345_sensor_handle_interrupt(struct sensor * sensor);
static int adxl345_sensor_clear_low_thresh(struct sensor *sensor,
                                           sensor_type_t type);
static int adxl345_sensor_clear_high_thresh(struct sensor *sensor,
                                            sensor_type_t type);


static const struct sensor_driver adxl345_sensor_driver = {
     .sd_read                      = adxl345_sensor_read,
     .sd_get_config                = adxl345_sensor_get_config,
     .sd_set_trigger_thresh        = adxl345_sensor_set_trigger_thresh,
     .sd_set_notification          = adxl345_sensor_set_notification,
     .sd_unset_notification        = adxl345_sensor_unset_notification,
     .sd_handle_interrupt          = adxl345_sensor_handle_interrupt,
     .sd_clear_low_trigger_thresh  = adxl345_sensor_clear_low_thresh,
     .sd_clear_high_trigger_thresh = adxl345_sensor_clear_high_thresh
};

/**
 * Writes a single byte to the specified register using i2c
 *
 * @param The sensor interface
 * @param The register address to write to
 * @param The value to write
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_i2c_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) {
        STATS_INC(g_adxl345stats, write_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 / 10, 1,
                           MYNEWT_VAL(ADXL345_I2C_RETRIES));

    if (rc) {
        ADXL345_LOG_ERROR(
                    "Failed to write to 0x%02X:0x%02X with value 0x%02X\n",
                    itf->si_addr, reg, value);
        STATS_INC(g_adxl345stats, read_errors);
    }
#endif

    return rc;
}

/**
 * Reads a single byte from the specified register using i2c
 *
 * @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
adxl345_i2c_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) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    struct hal_i2c_master_data data_struct = {
        .address = itf->si_addr,
        .len = 1,
        .buffer = &reg
    };

    /* Register write */
    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                           MYNEWT_VAL(ADXL345_I2C_RETRIES));
    if (rc) {
        ADXL345_LOG_ERROR("I2C access failed at address 0x%02X\n",
                    itf->si_addr);
                    
        STATS_INC(g_adxl345stats, write_errors);
        return rc;
    }
    
    /* Read one byte back */
    data_struct.buffer = value;
    rc = i2cn_master_read(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                          MYNEWT_VAL(ADXL345_I2C_RETRIES));

    if (rc) {
        ADXL345_LOG_ERROR("Failed to read from 0x%02X:0x%02X - %02X\n",
                    itf->si_addr, reg, rc);
        STATS_INC(g_adxl345stats, read_errors);
    }
#endif

    return rc;
}

/**
 * Read multiple bytes starting from specified register over i2c
 *    
 * @param The sensor interface
 * @param The register address start reading from
 * @param Pointer to where the register value should be written
 * @param Number of bytes to read
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_i2c_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) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    struct hal_i2c_master_data data_struct = {
        .address = itf->si_addr,
        .len = 1,
        .buffer = &reg
    };

    /* Register write */
    rc = i2cn_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                           MYNEWT_VAL(ADXL345_I2C_RETRIES));
    if (rc) {
        ADXL345_LOG_ERROR("I2C access failed at address 0x%02X\n",
                    itf->si_addr);
        STATS_INC(g_adxl345stats, write_errors);
        return rc;
    }

    /* Read data */
    data_struct.len = len;
    data_struct.buffer = buffer;
    rc = i2cn_master_read(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 1,
                          MYNEWT_VAL(ADXL345_I2C_RETRIES));

    if (rc) {
        ADXL345_LOG_ERROR("Failed to read from 0x%02X:0x%02X\n",
                    itf->si_addr, reg);
        STATS_INC(g_adxl345stats, read_errors);
    }
#endif

    return rc;
}

/**
 * Writes a single byte to the specified register using SPI
 *
 * @param The sensor interface
 * @param The register address to write to
 * @param The value to write
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_spi_write8(struct sensor_itf *itf, uint8_t reg, uint8_t value)
{
    int rc;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    uint8_t payload[2] = { reg, value };
    rc = bus_node_simple_write(itf->si_dev, payload, 2);
    if (rc) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    /* Select the device */
    hal_gpio_write(itf->si_cs_pin, 0);

    /* Send the address */
    rc = hal_spi_tx_val(itf->si_num, reg & ~ADXL345_SPI_READ_CMD_BIT);
    if (rc == 0xFFFF) {
        rc = SYS_EINVAL;
        ADXL345_LOG_ERROR("SPI_%u register write failed addr:0x%02X\n",
                    itf->si_num, reg);
        STATS_INC(g_adxl345stats, write_errors);
        goto err;
    }

    /* Read data */
    rc = hal_spi_tx_val(itf->si_num, value);
    if (rc == 0xFFFF) {
        rc = SYS_EINVAL;
        ADXL345_LOG_ERROR("SPI_%u write failed addr:0x%02X\n",
                    itf->si_num, reg);
        STATS_INC(g_adxl345stats, write_errors);
        goto err;
    }

    rc = 0;

err:
    /* De-select the device */
    hal_gpio_write(itf->si_cs_pin, 1);
#endif

    return rc;
}


/**
 * Reads a single byte from the specified register using SPI
 *

 * @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
adxl345_spi_read8(struct sensor_itf *itf, uint8_t reg, uint8_t *value)
{
    int rc = 0;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    reg |= ADXL345_SPI_READ_CMD_BIT;
    rc = bus_node_simple_write_read_transact(itf->si_dev, &reg, 1, value, 1);
    if (rc) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    uint16_t retval;

    /* Select the device */
    hal_gpio_write(itf->si_cs_pin, 0);

    /* Send the address */
    retval = hal_spi_tx_val(itf->si_num, reg | ADXL345_SPI_READ_CMD_BIT);
    
    if (retval == 0xFFFF) {
        rc = SYS_EINVAL;
        ADXL345_LOG_ERROR("SPI_%u register write failed addr:0x%02X\n",
                    itf->si_num, reg);
        STATS_INC(g_adxl345stats, read_errors);
        goto err;
    }

    /* Read data */
    retval = hal_spi_tx_val(itf->si_num, 0);
    if (retval == 0xFFFF) {
        rc = SYS_EINVAL;
        ADXL345_LOG_ERROR("SPI_%u read failed addr:0x%02X\n",
                     itf->si_num, reg);
        STATS_INC(g_adxl345stats, read_errors);
        goto err;
    }
    *value = retval;
    
err:
    /* De-select the device */
    hal_gpio_write(itf->si_cs_pin, 1);
#endif

    return rc;
}

/**
 * Read multiple bytes starting from specified register over SPI
 *
 * @param The sensor interface
 * @param The register address start reading from
 * @param Pointer to where the register value should be written
 * @param Number of bytes to read
 *
 * @return 0 on success, non-zero on failure
 */
int
adxl345_spi_readlen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
                    uint8_t len)
{
    int rc = 0;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    reg |= ADXL345_SPI_READ_CMD_BIT | ADXL345_SPI_MULTIBYTE_CMD_BIT;
    rc = bus_node_simple_write_read_transact(itf->si_dev, &reg, 1, buffer, len);
    if (rc) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    int i;
    uint16_t retval;

    /* Select the device */
    hal_gpio_write(itf->si_cs_pin, 0);

    /* Send the address */
    retval = hal_spi_tx_val(itf->si_num, reg | ADXL345_SPI_READ_CMD_BIT
                            | ADXL345_SPI_MULTIBYTE_CMD_BIT);
    
    if (retval == 0xFFFF) {
        rc = SYS_EINVAL;
        ADXL345_LOG_ERROR("SPI_%u register write failed addr:0x%02X\n",
                    itf->si_num, reg);
        STATS_INC(g_adxl345stats, read_errors);
        goto err;
    }

    for (i = 0; i < len; i++) {
        /* Read data */
        retval = hal_spi_tx_val(itf->si_num, 0);
        if (retval == 0xFFFF) {
            rc = SYS_EINVAL;
            ADXL345_LOG_ERROR("SPI_%u read failed addr:0x%02X\n",
                        itf->si_num, reg);
            STATS_INC(g_adxl345stats, read_errors);
            goto err;
        }
        buffer[i] = retval;
    }

err:
    /* De-select the device */
    hal_gpio_write(itf->si_cs_pin, 1);
#endif

    return rc;
}


/**
 * Write byte to ADXL345 sensor over different interfaces
 *
 * @param The sensor interface
 * @param The register address to write to
 * @param The value to write
 *
 * @return 0 on success, non-zero on failure
 */
int
adxl345_write8(struct sensor_itf *itf, uint8_t reg, uint8_t value)
{
    int rc;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    uint8_t payload[2] = { reg, value };
    rc = bus_node_simple_write(itf->si_dev, payload, 2);
    if (rc) {
        STATS_INC(g_adxl345stats, write_errors);
    }
#else
    rc = sensor_itf_lock(itf, MYNEWT_VAL(ADXL345_ITF_LOCK_TMO));
    if (rc) {
        return rc;
    }

    if (itf->si_type == SENSOR_ITF_I2C) {
        rc = adxl345_i2c_write8(itf, reg, value);
    } else {
        rc = adxl345_spi_write8(itf, reg, value);
    }

    sensor_itf_unlock(itf);
#endif

    return rc;
}

/**
 * Read byte data from ADXL345 sensor over different interfaces
 *
 * @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 on failure
 */
int
adxl345_read8(struct sensor_itf *itf, uint8_t reg, uint8_t *value)
{
    int rc;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    struct adxl345 *dev = (struct adxl345 *)itf->si_dev;

    if (dev->node_is_spi) {
        reg |= ADXL345_SPI_READ_CMD_BIT;
    }

    rc = bus_node_simple_write_read_transact(itf->si_dev, &reg, 1, value, 1);
    if (rc) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    rc = sensor_itf_lock(itf, MYNEWT_VAL(ADXL345_ITF_LOCK_TMO));
    if (rc) {
        return rc;
    }

    if (itf->si_type == SENSOR_ITF_I2C) {
        rc = adxl345_i2c_read8(itf, reg, value);
    } else {
        rc = adxl345_spi_read8(itf, reg, value);
    }

    sensor_itf_unlock(itf);
#endif

    return rc;
}

/**
 * Read multiple bytes starting from specified register over different interfaces
 *
 * @param The sensor interface
 * @param The register address start reading from
 * @param Pointer to where the register value should be written
 * @param Number of bytes to read
 *
 * @return 0 on success, non-zero on failure
 */
int
adxl345_readlen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
                uint8_t len)
{
    int rc;

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
    struct adxl345 *dev = (struct adxl345 *)itf->si_dev;

    if (dev->node_is_spi) {
        reg |= ADXL345_SPI_READ_CMD_BIT | ADXL345_SPI_MULTIBYTE_CMD_BIT;
    }

    rc = bus_node_simple_write_read_transact(itf->si_dev, &reg, 1, buffer, len);
    if (rc) {
        STATS_INC(g_adxl345stats, read_errors);
    }
#else
    rc = sensor_itf_lock(itf, MYNEWT_VAL(ADXL345_ITF_LOCK_TMO));
    if (rc) {
        return rc;
    }

    if (itf->si_type == SENSOR_ITF_I2C) {
        rc = adxl345_i2c_readlen(itf, reg, buffer, len);
    } else {
        rc = adxl345_spi_readlen(itf, reg, buffer, len);
    }

    sensor_itf_unlock(itf);
#endif

    return rc;
}

/**
 * Sets ADXL345 into new power mode
 *
 * @param The sensor interface
 * @param Power mode to set
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_power_mode(struct sensor_itf *itf, enum adxl345_power_mode state)
{
    uint8_t reg;
    int rc;
  
    rc = adxl345_read8(itf, ADXL345_POWER_CTL, &reg);
    if (rc) {
        return rc;
    }

    reg &= 0xF3;
    reg |= ((uint8_t)state) << 2;
  
    return adxl345_write8(itf, ADXL345_POWER_CTL, (uint8_t)reg);
}

/**
 * Gets power mode ADXL345 is currently in
 *
 * @param The sensor interface
 * @param Pointer to store current power mode
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_power_mode(struct sensor_itf *itf, enum adxl345_power_mode *state)
{
    uint8_t reg;
    int rc;

    rc = adxl345_read8(itf, ADXL345_POWER_CTL, &reg);
    if (rc) {
        return rc;
    }

    reg &= 0xC;
    reg >>= 2;

    *state = (enum adxl345_power_mode)reg;

    return 0;
}

/**
 * Sets whether low power mode is enabled. Low power mode acheives lower
 * power consumption at the expense of higher noise, but is only effective
 * at sample rates between 12.5Hz and 400Hz
 *
 * @param The sensor interface
 * @param Setting to use for low power mode
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_low_power_enable(struct sensor_itf *itf, uint8_t enable)
{
    uint8_t reg;
    int rc;
  
    rc = adxl345_read8(itf, ADXL345_BW_RATE, &reg);
    if (rc) {
        return rc;
    }

    reg &= 0x0F;
    if (enable) {
        reg |= 0x10;
    }
  
    return adxl345_write8(itf, ADXL345_BW_RATE, (uint8_t)reg);
}

/**
 * Gets current setting of low power mode. Low power mode acheives lower
 * power consumption at the expense of higher noise, but is only effective
 * at sample rates between 12.5Hz and 400Hz
 *
 * @param The sensor interface
 * @param Pointer to store value
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_low_power_enable(struct sensor_itf *itf, uint8_t *enable)
{
    uint8_t reg;
    int rc;

    rc = adxl345_read8(itf, ADXL345_BW_RATE, &reg);
    if (rc) {
        return rc;
    }

    *enable = (reg & 0x10) != 0;

    return 0;
}

/**
 * Sets the accelerometer range to specified setting
 *
 * @param The sensor interface
 * @param accelerometer range to set
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_accel_range(struct sensor_itf *itf, enum adxl345_accel_range range)
{
    uint8_t reg;
    int rc;
  
    rc = adxl345_read8(itf, ADXL345_DATA_FORMAT, &reg);
    if (rc) {
        return rc;
    }

    reg &= 0xFC;
    reg |= (uint8_t)range;
  
    return adxl345_write8(itf, ADXL345_DATA_FORMAT, (uint8_t)reg);
}

/**
 * Gets the accelerometer range currently set
 *
 * @param The sensor interface
 * @param Pointer to location to store accelerometer range
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_accel_range(struct sensor_itf *itf, enum adxl345_accel_range *range)
{
    uint8_t reg;
    int rc;

    rc = adxl345_read8(itf, ADXL345_DATA_FORMAT, &reg);
    if (rc) {
        return rc;
    }

    *range = (enum adxl345_accel_range)(reg & 0x3);

    return 0;
}

/**
 * Sets new offsets in ADXL345
 *
 * @param The sensor interface
 * @param X offset
 * @param Y offset
 * @param Z offset
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_offsets(struct sensor_itf *itf, int8_t offset_x,
                    int8_t offset_y, int8_t offset_z)
{
    int rc;
  
    rc = adxl345_write8(itf, ADXL345_OFSX, offset_x);
    if (rc){
        return rc;
    }

    rc = adxl345_write8(itf, ADXL345_OFSY, offset_y);
    if (rc){
        return rc;
    }

    return adxl345_write8(itf, ADXL345_OFSZ, offset_z);
}

/**
 * Gets the offsets currently set
 *
 * @param The sensor interface
 * @param Pointer to location to store X offset
 * @param Pointer to location to store Y offset
 * @param Pointer to location to store Z offset
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_offsets(struct sensor_itf *itf, int8_t *offset_x,
                    int8_t *offset_y, int8_t *offset_z)
{

    int rc;
  
    rc = adxl345_read8(itf, ADXL345_OFSX, (uint8_t *)offset_x);
    if (rc){
        return rc;
    }

    rc = adxl345_read8(itf, ADXL345_OFSY, (uint8_t *)offset_y);
    if (rc){
        return rc;
    }

    return adxl345_read8(itf, ADXL345_OFSZ, (uint8_t *)offset_z);
}

/**
 * Sets new tap settings in ADXL345 sensor
 *
 * @param The sensor interface
 * @param Structure of new settings
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_tap_settings(struct sensor_itf *itf, struct adxl345_tap_settings settings)
{
    int rc;
    uint8_t enables = 0;
    
    rc = adxl345_write8(itf, ADXL345_THRESH_TAP, settings.threshold);
    if (rc) {
        return rc;
    }

    rc = adxl345_write8(itf, ADXL345_DUR, settings.duration);
    if (rc) {
        return rc;
    }

    rc = adxl345_write8(itf, ADXL345_LATENT, settings.latency);
    if (rc) {
        return rc;
    }

    rc = adxl345_write8(itf, ADXL345_WINDOW, settings.window);
    if (rc) {
        return rc;
    }

    enables |= settings.x_enable ? (1 << 2) : 0;
    enables |= settings.y_enable ? (1 << 1) : 0;
    enables |= settings.z_enable ? (1 << 0) : 0;
    enables |= settings.suppress ? (1 << 3) : 0;

    return adxl345_write8(itf, ADXL345_TAP_AXES, enables);
}

/**
 * Gets the current tap settings from ADXL345 sensor
 *
 * @param The sensor interface
 * @param Ponter to structure to store settings
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_tap_settings(struct sensor_itf *itf, struct adxl345_tap_settings *settings)
{
    int rc;
    uint8_t enables;
    
    rc = adxl345_read8(itf, ADXL345_THRESH_TAP, &(settings->threshold));
    if (rc) {
        return rc;
    }

    rc = adxl345_read8(itf, ADXL345_DUR, &(settings->duration));
    if (rc) {
        return rc;
    }

    rc = adxl345_read8(itf, ADXL345_LATENT, &(settings->latency));
    if (rc) {
        return rc;
    }

    rc = adxl345_read8(itf, ADXL345_WINDOW, &(settings->window));
    if (rc) {
        return rc;
    }

    rc = adxl345_read8(itf, ADXL345_TAP_AXES, &enables);
    if (rc) {
        return rc;
    }

    settings->x_enable = (enables & (1 << 2)) != 0;
    settings->y_enable = (enables & (1 << 1)) != 0;
    settings->z_enable = (enables & (1 << 0)) != 0;
    settings->suppress = (enables & (1 << 3)) != 0;

    return 0;
}

/**
 * Sets new threshold for triggering activity interrupt
 *
 * @param The sensor interface
 * @param threshold value (62.5mg/LSB)
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_active_threshold(struct sensor_itf *itf, uint8_t threshold)
{
    return adxl345_write8(itf, ADXL345_THRESH_ACT, threshold);
}

/**
 * Gets current threshold for triggering activity interrupt
 *
 * @param The sensor interface
 * @param Pointer to store threshold value (62.5mg/LSB)
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_active_threshold(struct sensor_itf *itf, uint8_t *threshold)
{
    return adxl345_read8(itf, ADXL345_THRESH_ACT, threshold);
}

/**
 * Sets new threshold and time for triggering inactivity interrupt
 *
 * @param The sensor interface
 * @param threshold value (62.5mg/LSB)
 * @param time value in seconds
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_inactive_settings(struct sensor_itf *itf, uint8_t threshold, uint8_t time)
{
    int rc;
  
    rc = adxl345_write8(itf, ADXL345_THRESH_INACT, threshold);
    if (rc){
        return rc;
    }

    return adxl345_write8(itf, ADXL345_TIME_INACT, time);
}

/**
 * Gets current threshold and time set for triggering inactivity interrupt
 *
 * @param The sensor interface
 * @param Pointer to store threshold value (62.5mg/LSB)
 * @param Ponter to store time value in seconds
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_inactive_settings(struct sensor_itf *itf, uint8_t *threshold, uint8_t *time)
{
    int rc;
  
    rc = adxl345_read8(itf, ADXL345_THRESH_INACT, threshold);
    if (rc){
        return rc;
    }

    return adxl345_read8(itf, ADXL345_TIME_INACT, time);
}

/**
 * Sets new activity/inactivity interrupt axis selections
 *
 * @param The sensor interface
 * @param Structure of new configuration
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_act_inact_enables(struct sensor_itf *itf, struct adxl345_act_inact_enables cfg)
{
    uint8_t reg = 0;

    reg |= cfg.inact_z     ? (1 << 0) : 0;
    reg |= cfg.inact_y     ? (1 << 1) : 0;
    reg |= cfg.inact_x     ? (1 << 2) : 0;
    reg |= cfg.inact_ac_dc ? (1 << 3) : 0;
    reg |= cfg.act_z       ? (1 << 4) : 0;
    reg |= cfg.act_y       ? (1 << 5) : 0;
    reg |= cfg.act_x       ? (1 << 6) : 0;
    reg |= cfg.act_ac_dc   ? (1 << 7) : 0;

    ADXL345_LOG_ERROR("act_inact = 0x%x\n", reg);
    
    return adxl345_write8(itf, ADXL345_ACT_INACT_CTL, reg);
    
}

/**
 * Sets new activity/inactivity interrupt axis selections
 *
 * @param The sensor interface
 * @param Pointer to structure to store configuration
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_act_inact_enables(struct sensor_itf *itf, struct adxl345_act_inact_enables *cfg)
{
    int rc;
    uint8_t reg;

    rc = adxl345_read8(itf, ADXL345_ACT_INACT_CTL, &reg);
    if (rc) {
        return rc;
    }

    cfg->inact_z     = (reg & (1 << 0)) != 0;
    cfg->inact_y     = (reg & (1 << 1)) != 0;
    cfg->inact_x     = (reg & (1 << 2)) != 0;
    cfg->inact_ac_dc = (reg & (1 << 3)) != 0;
    cfg->act_z       = (reg & (1 << 4)) != 0;
    cfg->act_y       = (reg & (1 << 5)) != 0;
    cfg->act_x       = (reg & (1 << 6)) != 0;
    cfg->act_ac_dc   = (reg & (1 << 7)) != 0;

    return 0;
}

/**
 * Sets new threshold and time for triggering freefall interrupt
 *
 * @param The sensor interface
 * @param threshold value (62.5mg/LSB)
 * @param time value in seconds
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_freefall_settings(struct sensor_itf *itf, uint8_t threshold, uint8_t time)
{
    int rc;
  
    rc = adxl345_write8(itf, ADXL345_THRESH_FF, threshold);
    if (rc){
        return rc;
    }

    return adxl345_write8(itf, ADXL345_TIME_FF, time);
}

/**
 * Gets current threshold and time set for triggering freefall interrupt
 *
 * @param The sensor interface
 * @param Pointer to store threshold value (62.5mg/LSB)
 * @param Ponter to store time value in seconds
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_freefall_settings(struct sensor_itf *itf, uint8_t *threshold, uint8_t *time)
{
    int rc;
  
    rc = adxl345_read8(itf, ADXL345_THRESH_FF, threshold);
    if (rc){
        return rc;
    }

    return adxl345_read8(itf, ADXL345_TIME_FF, time);
}

/**
 * Sets new sample rate for measurements
 *
 * @param The sensor interface
 * @param sample rate value
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_set_sample_rate(struct sensor_itf *itf, enum adxl345_sample_rate rate)
{
    return adxl345_write8(itf, ADXL345_BW_RATE, (uint8_t) rate);
}

/**
 * Gets current sample rate settings
 *
 * @param The sensor interface
 * @param Pointer to store rate value
 *
 * @return 0 on success, non-zero error on failure.
 */
int
adxl345_get_sample_rate(struct sensor_itf *itf, enum adxl345_sample_rate *rate)
{
    int rc;
    uint8_t reg;
  
    rc = adxl345_read8(itf, ADXL345_BW_RATE, &reg);
    if (rc){
        return rc;
    }

    *rate = (enum adxl345_sample_rate)(reg & 0xF);    
    return 0;
}

/**
 * Configures which interrupts are enabled and which interrupt pins they are 
 * mapped to.
 *
 * @param The sensor interface
 * @param which interrupts are enabled
 * @param which interrupts are mapped to which pin
 *
 * @return 0 on success, non-zero error on failure
 */
int
adxl345_setup_interrupts(struct sensor_itf *itf, uint8_t enables, uint8_t mapping)
{
    int rc;

    rc = adxl345_write8(itf, ADXL345_INT_MAP, mapping);
    if (rc) {
        return rc;
    }

    return adxl345_write8(itf, ADXL345_INT_ENABLE, enables);
}

/**
 * Clears interrupts (except DATA_READY, Watermark & Overrun which need data to
 * be read to clear). Provides status as output to identify which interrupts have
 * triggered.
 * 
 * @param The sensor interface
 * @param Pointer to store interrupt status
 *
 * @return 0 on success, non-zero error on failure 
 */
int adxl345_clear_interrupts(struct sensor_itf *itf, uint8_t *int_status)
{
    return adxl345_read8(itf, ADXL345_INT_SOURCE, int_status);
}

/**
 * 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
adxl345_init(struct os_dev *dev, void *arg)
{
    struct adxl345 *adxl;
    struct sensor *sensor;
    int rc;

    if (!arg || !dev) {
        return SYS_ENODEV;
    }

    adxl = (struct adxl345 *) dev;

    adxl->cfg.mask = SENSOR_TYPE_ALL;

    sensor = &adxl->sensor;

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

    rc = sensor_init(sensor, dev);
    if (rc) {
        return rc;
    }

    /* Add the accelerometer/gyroscope driver */
    rc = sensor_set_driver(sensor, SENSOR_TYPE_ACCELEROMETER,
                           (struct sensor_driver *) &adxl345_sensor_driver);
    if (rc) {
        return rc;
    }

    rc = sensor_set_interface(sensor, arg);
    if (rc) {
        return rc;
    }

    rc = sensor_mgr_register(sensor);
    if (rc) {
        return rc;
    }

#if !MYNEWT_VAL(BUS_DRIVER_PRESENT)
    if (sensor->s_itf.si_type == SENSOR_ITF_SPI) {
        rc = hal_spi_config(sensor->s_itf.si_num, &spi_adxl345_settings);
        if (rc == EINVAL) {
            return rc;
        }

        rc = hal_spi_enable(sensor->s_itf.si_num);
        if (rc) {
            return rc;
        }

        rc = hal_gpio_init_out(sensor->s_itf.si_cs_pin, 1);
        if (rc) {
            return rc;
        }
    }
#endif

#if MYNEWT_VAL(ADXL345_INT_ENABLE)
    adxl->pdd.read_ctx.srec_sensor = sensor;
    adxl->pdd.notify_ctx.snec_sensor = sensor;

    rc = init_intpin(adxl, interrupt_handler, sensor);
    if (rc != 0) {
        return rc;
    }
#endif

    
    return 0;
}

/**
 * Configure ADXL345 sensor
 *
 * @param Sensor device ADXL345 structure
 * @param Sensor device ADXL345 config
 *
 * @return 0 on success, non-zero on failure
 */
int
adxl345_config(struct adxl345 *dev, struct adxl345_cfg *cfg)
{
    int rc;
    struct sensor_itf *itf;

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

    /* Check device id is correct */
    uint8_t val = 0;
    rc = adxl345_read8(itf, ADXL345_DEVID, &val);
    if (rc) {
        return rc;
    }
    if (val != ADXL345_DEVID_VAL) {
        return SYS_EINVAL;
    }

    rc = adxl345_read8(itf, ADXL345_DATA_FORMAT, &val);
    if (rc) {
        return rc;
    }

    /* Set accelerometer into full resolution */
    val |= 0x08;

    /* Set interupt pin polarity to match local pin setting */
    if (dev->sensor.s_itf.si_ints[dev->pdd.int_num].active) {
        val &= ~0x20;
    } else {
        val |= 0x20;
    }

    rc = adxl345_write8(itf, ADXL345_DATA_FORMAT, val);
    if (rc) {
        return rc;
    } 
    
    /* Setup range as per config */
    rc = adxl345_set_accel_range(itf, cfg->accel_range);
    if (rc) {
        return rc;
    }
    dev->cfg.accel_range = cfg->accel_range;

    /* setup sample rate */
    rc = adxl345_set_sample_rate(itf, cfg->sample_rate);
    if (rc) {
        return rc;
    }
    dev->cfg.sample_rate = cfg->sample_rate;
        
    /* setup data offsets */
    rc = adxl345_set_offsets(itf, cfg->offset_x, cfg->offset_y, cfg->offset_z);
    if (rc) {
        return rc;
    }
    dev->cfg.offset_x = cfg->offset_x;
    dev->cfg.offset_y = cfg->offset_y;
    dev->cfg.offset_z = cfg->offset_z;

    /* setup tap detection settings */
    rc = adxl345_set_tap_settings(itf, cfg->tap_cfg);
    if (rc) {
        return rc;
    }
    dev->cfg.tap_cfg = cfg->tap_cfg;

    /* setup activity detection settings */
    rc = adxl345_set_active_threshold(itf, 0xFF);
    if (rc) {
        return rc;
    }
    
    rc = adxl345_set_inactive_settings(itf, 0, 0);
    if (rc) {
        return rc;
    }

    /* setup freefall settings */
    rc = adxl345_set_freefall_settings(itf, cfg->freefall_threshold, cfg->freefall_time);
    if (rc) {
        return rc;
    }
    dev->cfg.freefall_threshold = cfg->freefall_threshold;
    dev->cfg.freefall_time = cfg->freefall_time;

    /* setup low power mode */
    rc = adxl345_set_low_power_enable(itf, cfg->low_power_enable);
    if (rc) {
        return rc;
    }
    dev->cfg.low_power_enable = cfg->low_power_enable;
    
    /* setup default interrupt config */
    rc = adxl345_setup_interrupts(itf, 0, 0);
    if (rc) {
        return rc;
    }

    rc = adxl345_clear_interrupts(itf, &val);
    if (rc) {
        return rc;
    }
    
    /* Setup current power mode */
    rc = adxl345_set_power_mode(itf, cfg->power_mode);
    if (rc) {
        return rc;
    } 
    dev->cfg.power_mode = cfg->power_mode;

    
    rc = sensor_set_type_mask(&(dev->sensor), cfg->mask);
    if (rc) {
        return rc;
    }

    dev->cfg.mask = cfg->mask;

    return 0;
}

static float adxl345_convert_reg_to_ms2(int16_t val, float lsb_mg)
{
    float tmp = val * lsb_mg * 0.001; /* convert to g */
    return tmp * STANDARD_ACCEL_GRAVITY; /* convert to ms2 */
}

static uint8_t adxl345_convert_ms2_to_reg(float ms2, float lsb_mg)
{
    float tmp = (ms2 * 1000) / STANDARD_ACCEL_GRAVITY; /* convert to mg */
    tmp /= lsb_mg; /* convert to reg format */   
    return (uint8_t) tmp;
}

/**
 * Reads Accelerometer data from ADXL345 sensor
 *
 * @param Pointer to sensor interface
 * @param Pointer to structure to store result
 *
 * @return 0 on success, non-zero on failure
 */
int
adxl345_get_accel_data(struct sensor_itf *itf, struct sensor_accel_data *sad)
{
    int rc;

    uint8_t payload[6];
    int16_t x,y,z;

    float lsb_mg = 4; /* lsb is always 4mg when device is set to full resolution */

    /* Get a new accelerometer sample */
    rc = adxl345_readlen(itf, ADXL345_DATAX0, payload, 6);
    if (rc) {
        return rc;
    }

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

    /* calculate MS*2 values to provide to data_func */
    sad->sad_x = adxl345_convert_reg_to_ms2(x, lsb_mg);
    sad->sad_x_is_valid = 1;
    sad->sad_y = adxl345_convert_reg_to_ms2(y, lsb_mg);
    sad->sad_y_is_valid = 1;
    sad->sad_z = adxl345_convert_reg_to_ms2(z, lsb_mg);
    sad->sad_z_is_valid = 1;

    return 0;
}

/**
 * Read Accelerometer data from ADXL345 sensor
 *
 * @param Pointer to sensor structure
 * @param Type of sensor data to read
 * @param Pointer to function to call to output data
 * @param Pointer to data to pass to data_func
 * @param timeout value
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_read(struct sensor *sensor, sensor_type_t type,
                    sensor_data_func_t data_func, void *data_arg, uint32_t timeout)
{
    (void)timeout;
    int rc;

    struct sensor_itf *itf;
    struct sensor_accel_data sad;
  
    /* If the read isn't looking for accel don't do anything. */
    if (!(type & SENSOR_TYPE_ACCELEROMETER)) {
        return SYS_EINVAL;
    }
  
    itf = SENSOR_GET_ITF(sensor);
  
    rc = adxl345_get_accel_data(itf, &sad);
    if (rc) {
        return rc;
    }

    /* output data using data_func */
    rc = data_func(sensor, data_arg, &sad, SENSOR_TYPE_ACCELEROMETER);
    if (rc) {
        return rc;
    }
  
    return 0;
}

static int
adxl345_sensor_get_config(struct sensor *sensor, sensor_type_t type,
                          struct sensor_cfg *cfg)
{
    /* If the read isn't looking for accel, don't do anything. */
    if (!(type & SENSOR_TYPE_ACCELEROMETER)) {
        return SYS_EINVAL;
    }

    cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT_TRIPLET;

    return 0;
}

#if MYNEWT_VAL(ADXL345_INT_ENABLE)
static void
interrupt_handler(void * arg)
{
    struct sensor *sensor = arg;
    sensor_mgr_put_interrupt_evt(sensor);
}

/**
 * Initialises the local interrupt pin 
 *
 * @param Pointer to device structure
 * @param interrupt handler to setup
 * @param Pointer to data to pass to irq
 *
 * @return 0 on success, non-zero on failure
 */
static int
init_intpin(struct adxl345 * adxl345, hal_gpio_irq_handler_t handler,
            void * arg)
{
    struct adxl345_private_driver_data *pdd = &adxl345->pdd;
    hal_gpio_irq_trig_t trig;
    int pin = -1;
    int rc;
    int i;

    for (i = 0; i < MYNEWT_VAL(SENSOR_MAX_INTERRUPTS_PINS); i++){
        pin = adxl345->sensor.s_itf.si_ints[i].host_pin;
        if (pin >= 0) {
            break;
        }
    }

    if (pin < 0) {
        ADXL345_LOG_ERROR("Interrupt pin not configured\n");
        return SYS_EINVAL;
    }

    pdd->int_num = i;
    if (adxl345->sensor.s_itf.si_ints[pdd->int_num].active) {
        trig = HAL_GPIO_TRIG_RISING;
    } else {
        trig = HAL_GPIO_TRIG_FALLING;
    }
  
    if (adxl345->sensor.s_itf.si_ints[pdd->int_num].device_pin == 1) {
        pdd->int_route = 0x00;
    } else if (adxl345->sensor.s_itf.si_ints[pdd->int_num].device_pin == 2) {
        pdd->int_route = 0xFF;
    } else {
        ADXL345_LOG_ERROR("Route not configured\n");
        return SYS_EINVAL;
    }

    rc = hal_gpio_irq_init(pin,
                           handler,
                           arg,
                           trig,
                           HAL_GPIO_PULL_NONE);
    if (rc != 0) {
        ADXL345_LOG_ERROR("Failed to initialise interrupt pin %d\n", pin);
        return rc;
    } 
    
    return 0;
}

/**
 * Enables an interrupt in ADXL345 device
 *
 * @param Pointer to sensor structure
 * @param which interrupt(s) to enable
 *
 * @return 0 on success, non-zero on failure
 */
static int
enable_interrupt(struct sensor * sensor, uint8_t ints_to_enable)
{
    struct adxl345_private_driver_data *pdd;
    struct adxl345 *adxl345;
    struct sensor_itf *itf;

    if (ints_to_enable == 0) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);
    itf = SENSOR_GET_ITF(sensor);
    pdd = &adxl345->pdd;
 
    /* if no interrupts are currently in use enable int pin */
    if (pdd->int_enable == 0) {
        hal_gpio_irq_enable(adxl345->sensor.s_itf.si_ints[pdd->int_num].host_pin);
    }
    
    /* update which interrupts are enabled */
    pdd->int_enable |= ints_to_enable;

    /* enable interrupt in device */
    return adxl345_setup_interrupts(itf, pdd->int_enable, pdd->int_route);
}

/**
 * Disables an interrupt in ADXL345 device
 *
 * @param Pointer to sensor structure
 * @param which interrupt(s) to disable
 *
 * @return 0 on success, non-zero on failure
 */
static int
disable_interrupt(struct sensor * sensor, uint8_t ints_to_disable)
{
    struct adxl345_private_driver_data *pdd;
    struct adxl345 *adxl345;
    struct sensor_itf *itf;

    if (ints_to_disable == 0) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);
    itf = SENSOR_GET_ITF(sensor);
    pdd = &adxl345->pdd;
    
    /* update which interrupts are enabled */
    pdd->int_enable &= ~ints_to_disable;

    /* if no interrupts are now in use disable int pin */
    if (pdd->int_enable == 0) {
        hal_gpio_irq_disable(adxl345->sensor.s_itf.si_ints[pdd->int_num].host_pin);
    }

    /* update interrupt setup in device */
    return adxl345_setup_interrupts(itf, pdd->int_enable, pdd->int_route);
}
#endif

/**
 * Handles and interrupt, firing correct events
 *
 * @param Pointer to sensor structure
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_handle_interrupt(struct sensor * sensor)
{
#if MYNEWT_VAL(ADXL345_INT_ENABLE)
    struct adxl345 * adxl345;
    struct adxl345_private_driver_data *pdd;
    struct sensor_itf *itf;
    uint8_t int_status;
    
    int rc;

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);
    itf = SENSOR_GET_ITF(sensor);

    pdd = &adxl345->pdd;

    rc = adxl345_clear_interrupts(itf, &int_status);
    if (rc != 0) {
        ADXL345_LOG_ERROR("Cound not read int status err=0x%02x\n", rc);
        return rc;
    }

    if (pdd->registered_mask & ADXL345_NOTIFY_MASK) {
        if (int_status & ADXL345_INT_SINGLE_TAP_BIT) {
            sensor_mgr_put_notify_evt(&pdd->notify_ctx, SENSOR_EVENT_TYPE_SINGLE_TAP);
        }

        if (int_status & ADXL345_INT_DOUBLE_TAP_BIT) {
            sensor_mgr_put_notify_evt(&pdd->notify_ctx, SENSOR_EVENT_TYPE_DOUBLE_TAP);
        }
    }

    if ((pdd->registered_mask & ADXL345_READ_MASK) &&
        ((int_status & ADXL345_INT_ACTIVITY_BIT) ||
         (int_status & ADXL345_INT_INACTIVITY_BIT))) {
        ADXL345_LOG_ERROR("READ EVT 0x%02x\n", int_status);
        sensor_mgr_put_read_evt(&pdd->read_ctx);
    }

    return 0;
#else
    return SYS_ENODEV;
#endif
}

/**
 * Sets up trigger thresholds and enables interrupts
 *
 * @param Pointer to sensor structure
 * @param type of sensor
 * @param threshold settings to configure
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_set_trigger_thresh(struct sensor * sensor,
                                  sensor_type_t sensor_type,
                                  struct sensor_type_traits * stt)
{
#if MYNEWT_VAL(ADXL345_INT_ENABLE)
    struct adxl345 * adxl345;
    struct sensor_itf *itf;
    int rc;
    const struct sensor_accel_data * low_thresh;
    const struct sensor_accel_data * high_thresh;
    uint8_t ints_to_enable = 0;
    float thresh;
    struct adxl345_private_driver_data *pdd;
    struct adxl345_act_inact_enables axis_enables = { 0 };

    if (sensor_type != SENSOR_TYPE_ACCELEROMETER) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);
    itf = SENSOR_GET_ITF(sensor);
    pdd = &adxl345->pdd;

    low_thresh  = stt->stt_low_thresh.sad;
    high_thresh = stt->stt_high_thresh.sad;

    if (low_thresh->sad_x_is_valid |
        low_thresh->sad_y_is_valid |
        low_thresh->sad_z_is_valid) {
        thresh = INFINITY;

        if (low_thresh->sad_x_is_valid) {
            axis_enables.inact_x = 1;
            if (thresh > low_thresh->sad_x) {
                thresh = low_thresh->sad_x;
            }
        }
        if (low_thresh->sad_y_is_valid) {
            axis_enables.inact_y = 1;
            if (thresh > low_thresh->sad_y) {
                thresh = low_thresh->sad_y;
            }
        }
        if (low_thresh->sad_z_is_valid) {
            axis_enables.inact_z = 1;
            if (thresh > low_thresh->sad_z) {
                thresh = low_thresh->sad_z;
            }
        }

        rc = adxl345_set_inactive_settings(itf,
                adxl345_convert_ms2_to_reg(thresh, 62.5), 2);
        
        if (rc) {
            return rc;
        }

        ints_to_enable |= ADXL345_INT_INACTIVITY_BIT;
    }

    if (high_thresh->sad_x_is_valid |
        high_thresh->sad_y_is_valid |
        high_thresh->sad_z_is_valid) {
        thresh = 0.0;

        if (high_thresh->sad_x_is_valid) {
            axis_enables.act_x = 1;
            if (thresh < high_thresh->sad_x) {
                thresh = high_thresh->sad_x;
            }
        }
        if (high_thresh->sad_y_is_valid) {
            axis_enables.act_y = 1;
            if (thresh < high_thresh->sad_y) {
                thresh = high_thresh->sad_y;
            }
        }
        if (high_thresh->sad_z_is_valid) {
            axis_enables.act_z = 1;
            if (thresh < high_thresh->sad_z) {
                thresh = high_thresh->sad_z;
            }
        }

        rc = adxl345_set_active_threshold(itf,
                   adxl345_convert_ms2_to_reg(thresh, 62.5));
        if (rc) {
            return rc;
        }

        ints_to_enable |= ADXL345_INT_ACTIVITY_BIT;
       
    }

    rc = enable_interrupt(sensor, ints_to_enable);
    if (rc) {
        return rc;
    }

    rc = adxl345_set_act_inact_enables(itf, axis_enables);
    if (rc) {
        return rc;
    }
    
    pdd->read_ctx.srec_type |= sensor_type;
    pdd->registered_mask |= ADXL345_READ_MASK;

    return 0;
#else
    return SYS_ENODEV;
#endif
}

/**
 * Disable the low threshold interrupt
 *
 * @param ptr to sensor
 * @param the Sensor type
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_clear_low_thresh(struct sensor *sensor,
                                 sensor_type_t type)
{
    struct adxl345 *adxl345;
    uint8_t ints_to_disable = ADXL345_INT_INACTIVITY_BIT;

    if (type != SENSOR_TYPE_ACCELEROMETER) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);

    /* if neither high or low threshs are now set disable read mask */
    if((adxl345->pdd.int_enable & ADXL345_INT_ACTIVITY_BIT) == 0) {
        adxl345->pdd.read_ctx.srec_type &= ~type;
        adxl345->pdd.registered_mask &= ~ADXL345_READ_MASK;
    }

    return disable_interrupt(sensor, ints_to_disable);
}


/**
 * Disable the high threshold interrupt
 *
 * @param ptr to sensor
 * @param the Sensor type
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_clear_high_thresh(struct sensor *sensor,
                                  sensor_type_t type)
{
    struct adxl345 *adxl345;
    uint8_t ints_to_disable = ADXL345_INT_ACTIVITY_BIT;

    if (type != SENSOR_TYPE_ACCELEROMETER) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);

    /* if neither high or low threshs are now set disable read mask */
    if((adxl345->pdd.int_enable & ADXL345_INT_INACTIVITY_BIT) == 0) {
        adxl345->pdd.read_ctx.srec_type &= ~type;
        adxl345->pdd.registered_mask &= ~ADXL345_READ_MASK;
    }

    return disable_interrupt(sensor, ints_to_disable);
}

/**
 * Enables notifications on tap or double tap events
 *
 * @param Pointer to sensor structure
 * @param which event to get notifications for
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_set_notification(struct sensor * sensor,
                                sensor_event_type_t sensor_event_type)
{
#if MYNEWT_VAL(ADXL345_INT_ENABLE)
    struct adxl345 * adxl345;
    uint8_t ints_to_enable = 0;
    struct adxl345_private_driver_data *pdd;
    int rc;

    ADXL345_LOG_ERROR("Enabling notifications\n");
    
    if ((sensor_event_type & ~(SENSOR_EVENT_TYPE_DOUBLE_TAP |
                               SENSOR_EVENT_TYPE_SINGLE_TAP)) != 0) {
        return SYS_EINVAL;
    }

    /*XXX for now we do not support registering for both events */
    if (sensor_event_type == (SENSOR_EVENT_TYPE_DOUBLE_TAP |
                                        SENSOR_EVENT_TYPE_SINGLE_TAP)) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);
    pdd = &adxl345->pdd;

    if (pdd->registered_mask & ADXL345_NOTIFY_MASK) {
        return SYS_EBUSY;
    }

    /* Enable tap event*/
    if(sensor_event_type == SENSOR_EVENT_TYPE_DOUBLE_TAP) {
        ints_to_enable |= ADXL345_INT_DOUBLE_TAP_BIT;
    }
    if(sensor_event_type == SENSOR_EVENT_TYPE_SINGLE_TAP) {
        ints_to_enable |= ADXL345_INT_SINGLE_TAP_BIT;
    }

    rc = enable_interrupt(sensor, ints_to_enable);
    if (rc) {
        return rc;
    }

    pdd->notify_ctx.snec_evtype |= sensor_event_type;
    pdd->registered_mask |= ADXL345_NOTIFY_MASK;

    ADXL345_LOG_ERROR("Enabled notifications\n");
    
    return 0;
#else
    return SYS_ENODEV;
#endif
}


/**
 * Disables notifications on tap or double tap events
 *
 * @param Pointer to sensor structure
 * @param which event to get notifications for
 *
 * @return 0 on success, non-zero on failure
 */
static int
adxl345_sensor_unset_notification(struct sensor * sensor,
                                  sensor_event_type_t sensor_event_type)
{
#if MYNEWT_VAL(ADXL345_INT_ENABLE)
    struct adxl345 * adxl345;
    uint8_t ints_to_disable = 0;

    if ((sensor_event_type & ~(SENSOR_EVENT_TYPE_DOUBLE_TAP |
                               SENSOR_EVENT_TYPE_SINGLE_TAP)) != 0) {
        return SYS_EINVAL;
    }

    /*XXX for now we do not support registering for both events */
    if (sensor_event_type == (SENSOR_EVENT_TYPE_DOUBLE_TAP |
                                        SENSOR_EVENT_TYPE_SINGLE_TAP)) {
        return SYS_EINVAL;
    }

    adxl345 = (struct adxl345 *)SENSOR_GET_DEVICE(sensor);

    adxl345->pdd.notify_ctx.snec_evtype &= ~sensor_event_type;
    adxl345->pdd.registered_mask &= ~ADXL345_NOTIFY_MASK;

    ints_to_disable |= ADXL345_INT_SINGLE_TAP_BIT;
    ints_to_disable |= ADXL345_INT_DOUBLE_TAP_BIT;
   
    return disable_interrupt(sensor, ints_to_disable);
    
#else
    return SYS_ENODEV;
#endif
}

#if MYNEWT_VAL(BUS_DRIVER_PRESENT)
static void
init_node_cb(struct bus_node *bnode, void *arg)
{
    struct sensor_itf *itf = arg;

    adxl345_init((struct os_dev *)bnode, itf);
}

int
adxl345_create_i2c_sensor_dev(struct bus_i2c_node *node, const char *name,
                              const struct bus_i2c_node_cfg *i2c_cfg,
                              struct sensor_itf *sensor_itf)
{
    struct adxl345 *dev = (struct adxl345 *)node;
    struct bus_node_callbacks cbs = {
        .init = init_node_cb,
    };
    int rc;

    dev->node_is_spi = false;

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

    rc = bus_i2c_node_create(name, node, i2c_cfg, sensor_itf);

    return rc;
}

int
adxl345_create_spi_sensor_dev(struct bus_spi_node *node, const char *name,
                              const struct bus_spi_node_cfg *spi_cfg,
                              struct sensor_itf *sensor_itf)
{
    struct adxl345 *dev = (struct adxl345 *)node;
    struct bus_node_callbacks cbs = {
        .init = init_node_cb,
    };
    int rc;

    dev->node_is_spi = true;

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

    rc = bus_spi_node_create(name, node, spi_cfg, sensor_itf);

    return rc;
}
#endif
