C++程序  |  3256行  |  133.69 KB

/*
 * Copyright (C) 2016 STMicroelectronics
 *
 * Author: Denis Ciocca <denis.ciocca@st.com>
 *
 * Licensed 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 <stdlib.h>
#include <string.h>
#include <sensors.h>
#include <slab.h>
#include <spi.h>
#include <gpio.h>
#include <atomic.h>
#include <timer.h>
#include <printf.h>
#include <isr.h>
#include <hostIntf.h>
#include <nanohubPacket.h>
#include <cpu/cpuMath.h>
#include <variant/sensType.h>
#include <plat/gpio.h>
#include <plat/syscfg.h>
#include <plat/exti.h>
#include <plat/rtc.h>
#include <algos/accel_cal.h>
#include <algos/gyro_cal.h>
#include <algos/mag_cal.h>

#include "st_lsm6dsm_lis3mdl_slave.h"
#include "st_lsm6dsm_lsm303agr_slave.h"
#include "st_lsm6dsm_ak09916_slave.h"
#include "st_lsm6dsm_lps22hb_slave.h"

#if defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) || defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)
#define LSM6DSM_I2C_MASTER_ENABLED                    1
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

#if defined(LSM6DSM_MAGN_CALIB_ENABLED) && !defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED)
#pragma message("LSM6DSM_MAGN_CALIB_ENABLED can not be used if no magnetometer sensors are enabled on I2C master. Disabling it!")
#undef LSM6DSM_MAGN_CALIB_ENABLED
#endif /* LSM6DSM_MAGN_CALIB_ENABLED, LSM6DSM_I2C_MASTER_ENABLED */

#if defined(LSM6DSM_I2C_MASTER_USE_INTERNAL_PULLUP) && !defined(LSM6DSM_I2C_MASTER_ENABLED)
#pragma message("LSM6DSM_I2C_MASTER_USE_INTERNAL_PULLUP has no meaning if no sensors are enabled on I2C master. Discarding!")
#endif /* LSM6DSM_I2C_MASTER_USE_INTERNAL_PULLUP, LSM6DSM_I2C_MASTER_ENABLED */

#define LSM6DSM_APP_ID                                APP_ID_MAKE(NANOHUB_VENDOR_STMICRO, 0)

#define LSM6DSM_WAI_VALUE                             (0x6a)
#define LSM6DSM_RETRY_CNT_WAI                         5
#define LSM6DSM_ACCEL_KSCALE                          0.00239364f    /* Accel scale @8g in (m/s^2)/LSB */
#define LSM6DSM_GYRO_KSCALE                           0.00122173f    /* Gyro scale @2000dps in (rad/sec)/LSB */
#define LSM6DSM_ONE_SAMPLE_BYTE                       6              /* One sample on triaxial sensor generate 6 byte */
#define LSM6DSM_TEMP_SAMPLE_BYTE                      2              /* One sample on temperature sensor generate 2 byte */
#define LSM6DSM_TEMP_OFFSET                           (25.0f)

/* Sensors orientation */
#define LSM6DSM_ROT_MATRIX                            1, 0, 0, 0, 1, 0, 0, 0, 1
#define LSM6DSM_MAGN_ROT_MATRIX                       1, 0, 0, 0, 1, 0, 0, 0, 1

/* SPI slave connection */
#define LSM6DSM_SPI_SLAVE_BUS_ID                      1
#define LSM6DSM_SPI_SLAVE_FREQUENCY_HZ                10000000
#define LSM6DSM_SPI_SLAVE_CS_GPIO                     GPIO_PB(12)

/* LSM6DSM status check registers */
#define LSM6DSM_STATUS_REG_XLDA                       (0x01)
#define LSM6DSM_STATUS_REG_GDA                        (0x02)
#define LSM6DSM_STATUS_REG_TDA                        (0x04)
#define LSM6DSM_FUNC_SRC_STEP_DETECTED                (0x10)
#define LSM6DSM_FUNC_SRC_STEP_COUNT_DELTA_IA          (0x80)
#define LSM6DSM_FUNC_SRC_SIGN_MOTION                  (0x40)
#define LSM6DSM_FUNC_SRC_SENSOR_HUB_END_OP            (0x01)

/* LSM6DSM ODR related */
#define LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON            80000
#define LSM6DSM_ODR_12HZ_ACCEL_STD                    1
#define LSM6DSM_ODR_26HZ_ACCEL_STD                    1
#define LSM6DSM_ODR_52HZ_ACCEL_STD                    1
#define LSM6DSM_ODR_104HZ_ACCEL_STD                   1
#define LSM6DSM_ODR_208HZ_ACCEL_STD                   1
#define LSM6DSM_ODR_416HZ_ACCEL_STD                   1
#define LSM6DSM_ODR_12HZ_GYRO_STD                     2
#define LSM6DSM_ODR_26HZ_GYRO_STD                     3
#define LSM6DSM_ODR_52HZ_GYRO_STD                     3
#define LSM6DSM_ODR_104HZ_GYRO_STD                    3
#define LSM6DSM_ODR_208HZ_GYRO_STD                    3
#define LSM6DSM_ODR_416HZ_GYRO_STD                    3

#define LSM6DSM_ODR_12HZ_REG_VALUE                    (0x10)
#define LSM6DSM_ODR_26HZ_REG_VALUE                    (0x20)
#define LSM6DSM_ODR_52HZ_REG_VALUE                    (0x30)
#define LSM6DSM_ODR_104HZ_REG_VALUE                   (0x40)
#define LSM6DSM_ODR_208HZ_REG_VALUE                   (0x50)
#define LSM6DSM_ODR_416HZ_REG_VALUE                   (0x60)

/* Interrupts */
#define LSM6DSM_INT_IRQ                               EXTI9_5_IRQn
#define LSM6DSM_INT1_GPIO                             GPIO_PB(6)
#define LSM6DSM_INT1_INDEX                            0
#define LSM6DSM_INT2_INDEX                            1
#define LSM6DSM_INT_NUM                               2

#define LSM6DSM_INT_ACCEL_ENABLE_REG_VALUE            (0x01)
#define LSM6DSM_INT_GYRO_ENABLE_REG_VALUE             (0x02)
#define LSM6DSM_INT_STEP_DETECTOR_ENABLE_REG_VALUE    (0x80)
#define LSM6DSM_INT_STEP_COUNTER_ENABLE_REG_VALUE     (0x80)
#define LSM6DSM_INT_SIGN_MOTION_ENABLE_REG_VALUE      (0x40)

/* LSM6DSM registers */
#define LSM6DSM_FUNC_CFG_ACCESS_ADDR                  (0x01)
#define LSM6DSM_DRDY_PULSE_CFG_ADDR                   (0x0b)
#define LSM6DSM_INT1_CTRL_ADDR                        (0x0d)
#define LSM6DSM_INT2_CTRL_ADDR                        (0x0e)
#define LSM6DSM_WAI_ADDR                              (0x0f)
#define LSM6DSM_CTRL1_XL_ADDR                         (0x10)
#define LSM6DSM_CTRL2_G_ADDR                          (0x11)
#define LSM6DSM_CTRL3_C_ADDR                          (0x12)
#define LSM6DSM_CTRL4_C_ADDR                          (0x13)
#define LSM6DSM_EBD_STEP_COUNT_DELTA_ADDR             (0x15)
#define LSM6DSM_CTRL10_C_ADDR                         (0x19)
#define LSM6DSM_MASTER_CONFIG_ADDR                    (0x1a)
#define LSM6DSM_STATUS_REG_ADDR                       (0x1e)
#define LSM6DSM_OUTX_L_G_ADDR                         (0x22)
#define LSM6DSM_OUTX_L_XL_ADDR                        (0x28)
#define LSM6DSM_OUT_TEMP_L_ADDR                       (0x20)
#define LSM6DSM_SENSORHUB1_REG_ADDR                   (0x2e)
#define LSM6DSM_STEP_COUNTER_L_ADDR                   (0x4b)
#define LSM6DSM_FUNC_SRC_ADDR                         (0x53)

#define LSM6DSM_SW_RESET                              (0x01)
#define LSM6DSM_RESET_PEDOMETER                       (0x02)
#define LSM6DSM_ENABLE_FUNC_CFG_ACCESS                (0x80)
#define LSM6DSM_ENABLE_DIGITAL_FUNC                   (0x04)
#define LSM6DSM_ENABLE_PEDOMETER_DIGITAL_FUNC         (0x10)
#define LSM6DSM_ENABLE_SIGN_MOTION_DIGITAL_FUNC       (0x01)
#define LSM6DSM_ENABLE_TIMER_DIGITAL_FUNC             (0x20)
#define LSM6DSM_MASTER_CONFIG_PULL_UP_EN              (0x08)
#define LSM6DSM_MASTER_CONFIG_MASTER_ON               (0x01)
#define LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1            (0x80)

/* LSM6DSM embedded registers */
#define LSM6DSM_EMBEDDED_SLV0_ADDR_ADDR               (0x02)
#define LSM6DSM_EMBEDDED_SLV0_SUBADDR_ADDR            (0x03)
#define LSM6DSM_EMBEDDED_SLV0_CONFIG_ADDR             (0x04)
#define LSM6DSM_EMBEDDED_SLV1_ADDR_ADDR               (0x05)
#define LSM6DSM_EMBEDDED_SLV1_SUBADDR_ADDR            (0x06)
#define LSM6DSM_EMBEDDED_SLV1_CONFIG_ADDR             (0x07)
#define LSM6DSM_EMBEDDED_SLV2_ADDR_ADDR               (0x08)
#define LSM6DSM_EMBEDDED_SLV2_SUBADDR_ADDR            (0x09)
#define LSM6DSM_EMBEDDED_SLV2_CONFIG_ADDR             (0x0a)
#define LSM6DSM_EMBEDDED_SLV3_ADDR_ADDR               (0x0b)
#define LSM6DSM_EMBEDDED_SLV3_SUBADDR_ADDR            (0x0c)
#define LSM6DSM_EMBEDDED_SLV3_CONFIG_ADDR             (0x0d)
#define LSM6DSM_EMBEDDED_DATAWRITE_SLV0_ADDR          (0x0e)
#define LSM6DSM_EMBEDDED_STEP_COUNT_DELTA_ADDR        (0x15)

#define LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB           (0x01)
#define LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_ONLY_WRITE   (0x00)
#define LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_ONE_SENSOR   (0x10)
#define LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_TWO_SENSOR   (0x20)
#define LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_THREE_SENSOR (0x30)
#define LSM6DSM_EMBEDDED_SLV1_CONFIG_WRITE_ONCE       (0x20)
#define LSM6DSM_EMBEDDED_SLV0_WRITE_ADDR_SLEEP        (0x07)

/* LSM6DSM I2C master - slave devices */
#ifdef LSM6DSM_I2C_MASTER_LIS3MDL
#define LSM6DSM_MAGN_KSCALE                           LIS3MDL_KSCALE
#define LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT       LIS3MDL_I2C_ADDRESS
#define LSM6DSM_SENSOR_SLAVE_MAGN_DUMMY_REG_ADDR      LIS3MDL_WAI_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_RESET_ADDR          LIS3MDL_CTRL2_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_RESET_VALUE         LIS3MDL_SW_RESET
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ADDR          LIS3MDL_CTRL3_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_BASE          LIS3MDL_CTRL3_BASE
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ON_VALUE      LIS3MDL_POWER_ON_VALUE
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE     LIS3MDL_POWER_OFF_VALUE
#define LSM6DSM_SENSOR_SLAVE_MAGN_ODR_ADDR            LIS3MDL_CTRL1_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_ODR_BASE            LIS3MDL_CTRL1_BASE
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_ADDR        LIS3MDL_OUTDATA_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN         LIS3MDL_OUTDATA_LEN
#define LSM6DSM_SENSOR_SLAVE_MAGN_RATES_REG_VALUE(i)  LIS3MDLMagnRatesRegValue[i]
#endif /* LSM6DSM_I2C_MASTER_LIS3MDL */

#ifdef LSM6DSM_I2C_MASTER_AK09916
#define LSM6DSM_MAGN_KSCALE                           AK09916_KSCALE
#define LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT       AK09916_I2C_ADDRESS
#define LSM6DSM_SENSOR_SLAVE_MAGN_DUMMY_REG_ADDR      AK09916_WAI_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_RESET_ADDR          AK09916_CNTL3_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_RESET_VALUE         AK09916_SW_RESET
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ADDR          AK09916_CNTL2_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_BASE          AK09916_CNTL2_BASE
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ON_VALUE      AK09916_POWER_ON_VALUE
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE     AK09916_POWER_OFF_VALUE
#define LSM6DSM_SENSOR_SLAVE_MAGN_ODR_ADDR            AK09916_CNTL2_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_ODR_BASE            AK09916_CNTL2_BASE
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_ADDR        AK09916_OUTDATA_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN         AK09916_OUTDATA_LEN
#define LSM6DSM_SENSOR_SLAVE_MAGN_RATES_REG_VALUE(i)  AK09916MagnRatesRegValue[i]
#endif /* LSM6DSM_I2C_MASTER_AK09916 */

#ifdef LSM6DSM_I2C_MASTER_LSM303AGR
#define LSM6DSM_MAGN_KSCALE                           LSM303AGR_KSCALE
#define LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT       LSM303AGR_I2C_ADDRESS
#define LSM6DSM_SENSOR_SLAVE_MAGN_DUMMY_REG_ADDR      LSM303AGR_WAI_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_RESET_ADDR          LSM303AGR_CFG_REG_A_M_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_RESET_VALUE         LSM303AGR_SW_RESET
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ADDR          LSM303AGR_CFG_REG_A_M_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_BASE          LSM303AGR_CFG_REG_A_M_BASE
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ON_VALUE      LSM303AGR_POWER_ON_VALUE
#define LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE     LSM303AGR_POWER_OFF_VALUE
#define LSM6DSM_SENSOR_SLAVE_MAGN_ODR_ADDR            LSM303AGR_CFG_REG_A_M_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_ODR_BASE            LSM303AGR_CFG_REG_A_M_BASE
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_ADDR        LSM303AGR_OUTDATA_ADDR
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN         LSM303AGR_OUTDATA_LEN
#define LSM6DSM_SENSOR_SLAVE_MAGN_RATES_REG_VALUE(i)  LSM303AGRMagnRatesRegValue[i]
#endif /* LSM6DSM_I2C_MASTER_LSM303AGR */

#ifdef LSM6DSM_I2C_MASTER_LPS22HB
#define LSM6DSM_PRESS_KSCALE                          LPS22HB_PRESS_KSCALE
#define LSM6DSM_TEMP_KSCALE                           LPS22HB_TEMP_KSCALE
#define LSM6DSM_PRESS_OUTDATA_LEN                     LPS22HB_OUTDATA_PRESS_BYTE
#define LSM6DSM_TEMP_OUTDATA_LEN                      LPS22HB_OUTDATA_TEMP_BYTE
#define LSM6DSM_SENSOR_SLAVE_BARO_I2C_ADDR_8BIT       LPS22HB_I2C_ADDRESS
#define LSM6DSM_SENSOR_SLAVE_BARO_DUMMY_REG_ADDR      LPS22HB_WAI_ADDR
#define LSM6DSM_SENSOR_SLAVE_BARO_RESET_ADDR          LPS22HB_CTRL2_ADDR
#define LSM6DSM_SENSOR_SLAVE_BARO_RESET_VALUE         LPS22HB_SW_RESET
#define LSM6DSM_SENSOR_SLAVE_BARO_POWER_ADDR          LPS22HB_CTRL1_ADDR
#define LSM6DSM_SENSOR_SLAVE_BARO_POWER_BASE          LPS22HB_CTRL1_BASE
#define LSM6DSM_SENSOR_SLAVE_BARO_POWER_ON_VALUE      LPS22HB_POWER_ON_VALUE
#define LSM6DSM_SENSOR_SLAVE_BARO_POWER_OFF_VALUE     LPS22HB_POWER_OFF_VALUE
#define LSM6DSM_SENSOR_SLAVE_BARO_ODR_ADDR            LPS22HB_CTRL1_ADDR
#define LSM6DSM_SENSOR_SLAVE_BARO_ODR_BASE            LPS22HB_CTRL1_BASE
#define LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_ADDR        LPS22HB_OUTDATA_ADDR
#define LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN         LPS22HB_OUTDATA_LEN
#define LSM6DSM_SENSOR_SLAVE_BARO_RATES_REG_VALUE(i)  LPS22HBBaroRatesRegValue[i]
#endif /* LSM6DSM_I2C_MASTER_LPS22HB */

#ifndef LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN
#define LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN         0
#endif /* LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN */
#ifndef LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN
#define LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN         0
#endif /* LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN */

#define LSM6DSM_SH_READ_BYTE_NUM                      (LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN + \
                                                      LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN)

/* SPI buffers */
#define LSM6DSM_SPI_PACKET_SIZE                       70
#define LSM6DSM_OUTPUT_DATA_READ_SIZE                 ((2 * LSM6DSM_ONE_SAMPLE_BYTE) + LSM6DSM_SH_READ_BYTE_NUM + 2)
#define LSM6DSM_BUF_MARGIN                            120
#define SPI_BUF_SIZE                                  (LSM6DSM_OUTPUT_DATA_READ_SIZE + LSM6DSM_BUF_MARGIN)

/* Magn & Baro both enabled */
#if defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)
#ifdef LSM6DSM_I2C_MASTER_AK09916
#define LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE         LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_THREE_SENSOR
#else /* LSM6DSM_I2C_MASTER_AK09916 */
#define LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE         LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_TWO_SENSOR
#endif /* LSM6DSM_I2C_MASTER_AK09916 */
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED) */

/* Magn only enabled */
#if defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && !defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)
#ifdef LSM6DSM_I2C_MASTER_AK09916
#define LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE         LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_TWO_SENSOR
#else /* LSM6DSM_I2C_MASTER_AK09916 */
#define LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE         LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_ONE_SENSOR
#endif /* LSM6DSM_I2C_MASTER_AK09916 */
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED) */

/* Baro only enabled */
#if !defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)
#define LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE         LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_ONE_SENSOR
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED) */

/* LSM6DSM default base registers status */
/* LSM6DSM_FUNC_CFG_ACCESS_BASE: enable embedded functions register */
#define LSM6DSM_FUNC_CFG_ACCESS_BASE                  (0x00)

/* LSM6DSM_DRDY_PULSE_CFG_BASE: enable pulsed interrupt register */
#define LSM6DSM_DRDY_PULSE_CFG_BASE                   (0x80)

/* LSM6DSM_INT1_CTRL_BASE: interrupt 1 control register default settings */
#define LSM6DSM_INT1_CTRL_BASE                       ((0 << 7) |    /* INT1_STEP_DETECTOR */ \
                                                      (0 << 6) |    /* INT1_SIGN_MOT */ \
                                                      (0 << 5) |    /* INT1_FULL_FLAG */ \
                                                      (0 << 4) |    /* INT1_FIFO_OVR */ \
                                                      (0 << 3) |    /* INT1_FTH */ \
                                                      (0 << 2) |    /* INT1_BOOT */ \
                                                      (0 << 1) |    /* INT1_DRDY_G */ \
                                                      (0 << 0))     /* INT1_DRDY_XL */

/* LSM6DSM_INT2_CTRL_BASE: interrupt 2 control register default settings */
#define LSM6DSM_INT2_CTRL_BASE                       ((0 << 7) |    /* INT2_STEP_DELTA */ \
                                                      (0 << 6) |    /* INT2_STEP_OV */ \
                                                      (0 << 5) |    /* INT2_FULL_FLAG */ \
                                                      (0 << 4) |    /* INT2_FIFO_OVR */ \
                                                      (0 << 3) |    /* INT2_FTH */ \
                                                      (0 << 2) |    /* INT2_DRDY_TEMP */ \
                                                      (0 << 1) |    /* INT2_DRDY_G */ \
                                                      (0 << 0))     /* INT2_DRDY_XL */

/* LSM6DSM_CTRL1_XL_BASE: accelerometer sensor register default settings */
#define LSM6DSM_CTRL1_XL_BASE                        ((0 << 7) |    /* ODR_XL3 */ \
                                                      (0 << 6) |    /* ODR_XL2 */ \
                                                      (0 << 5) |    /* ODR_XL1 */ \
                                                      (0 << 4) |    /* ODR_XL0 */ \
                                                      (1 << 3) |    /* FS_XL1 */ \
                                                      (1 << 2) |    /* FS_XL0 */ \
                                                      (0 << 1) |    /* LPF1_BW_SEL */ \
                                                      (0 << 0))     /* (0) */

/* LSM6DSM_CTRL2_G_BASE: gyroscope sensor register default settings */
#define LSM6DSM_CTRL2_G_BASE                         ((0 << 7) |    /* ODR_G3 */ \
                                                      (0 << 6) |    /* ODR_G2 */ \
                                                      (0 << 5) |    /* ODR_G1 */ \
                                                      (0 << 4) |    /* ODR_G0 */ \
                                                      (1 << 3) |    /* FS_G1 */ \
                                                      (1 << 2) |    /* FS_G0 */ \
                                                      (0 << 1) |    /* FS_125 */ \
                                                      (0 << 0))     /* (0) */

/* LSM6DSM_CTRL3_C_BASE: control register 3 default settings */
#define LSM6DSM_CTRL3_C_BASE                         ((0 << 7) |    /* BOOT */ \
                                                      (1 << 6) |    /* BDU */ \
                                                      (0 << 5) |    /* H_LACTIVE */ \
                                                      (0 << 4) |    /* PP_OD */ \
                                                      (0 << 3) |    /* SIM */ \
                                                      (1 << 2) |    /* IF_INC */ \
                                                      (0 << 1) |    /* BLE */ \
                                                      (0 << 0))     /* SW_RESET */

/* LSM6DSM_CTRL4_C_BASE: control register 4 default settings */
#define LSM6DSM_CTRL4_C_BASE                         ((0 << 7) |    /* DEN_XL_EN */ \
                                                      (0 << 6) |    /* SLEEP */ \
                                                      (1 << 5) |    /* INT2_on_INT1 */ \
                                                      (0 << 4) |    /* DEN_DRDY_MASK */ \
                                                      (0 << 3) |    /* DRDY_MASK */ \
                                                      (1 << 2) |    /* I2C_disable */ \
                                                      (0 << 1) |    /* LPF1_SEL_G */ \
                                                      (0 << 0))     /* (0) */

/* LSM6DSM_CTRL10_C_BASE: control register 10 default settings */
#define LSM6DSM_CTRL10_C_BASE                        ((0 << 7) |    /* (0) */ \
                                                      (0 << 6) |    /* (0) */ \
                                                      (0 << 5) |    /* TIMER_EN */ \
                                                      (0 << 4) |    /* PEDO_EN */ \
                                                      (0 << 3) |    /* TILT_EN */ \
                                                      (0 << 2) |    /* FUNC_EN */ \
                                                      (0 << 1) |    /* PEDO_RST_STEP */ \
                                                      (0 << 0))     /* SIGN_MOTION_EN */

/* LSM6DSM_MASTER_CONFIG_BASE: I2C master configuration register default value */
#ifdef LSM6DSM_I2C_MASTER_USE_INTERNAL_PULLUP
#define LSM6DSM_MASTER_CONFIG_BASE                    (LSM6DSM_MASTER_CONFIG_PULL_UP_EN)
#else /* LSM6DSM_I2C_MASTER_USE_INTERNAL_PULLUP */
#define LSM6DSM_MASTER_CONFIG_BASE                    (0x00)
#endif /* LSM6DSM_I2C_MASTER_USE_INTERNAL_PULLUP */

#define LSM6DSM_X_MAP(x, y, z, r11, r12, r13, r21, r22, r23, r31, r32, r33) \
                                                      ((r11 == 1 ? x : (r11 == -1 ? -x : 0)) + \
                                                      (r21 == 1 ? y : (r21 == -1 ? -y : 0)) + \
                                                      (r31 == 1 ? z : (r31 == -1 ? -z : 0)))

#define LSM6DSM_Y_MAP(x, y, z, r11, r12, r13, r21, r22, r23, r31, r32, r33) \
                                                      ((r12 == 1 ? x : (r12 == -1 ? -x : 0)) + \
                                                      (r22 == 1 ? y : (r22 == -1 ? -y : 0)) + \
                                                      (r32 == 1 ? z : (r32 == -1 ? -z : 0)))

#define LSM6DSM_Z_MAP(x, y, z, r11, r12, r13, r21, r22, r23, r31, r32, r33) \
                                                      ((r13 == 1 ? x : (r13 == -1 ? -x : 0)) + \
                                                      (r23 == 1 ? y : (r23 == -1 ? -y : 0)) + \
                                                      (r33 == 1 ? z : (r33 == -1 ? -z : 0)))

#define LSM6DSM_REMAP_X_DATA(...)                     LSM6DSM_X_MAP(__VA_ARGS__)
#define LSM6DSM_REMAP_Y_DATA(...)                     LSM6DSM_Y_MAP(__VA_ARGS__)
#define LSM6DSM_REMAP_Z_DATA(...)                     LSM6DSM_Z_MAP(__VA_ARGS__)

enum SensorIndex {
    ACCEL = 0,
    GYRO,
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    MAGN,
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    PRESS,
    TEMP,
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
    STEP_DETECTOR,
    STEP_COUNTER,
    SIGN_MOTION,
    NUM_SENSORS
};

enum InitState {
    RESET_LSM6DSM = 0,
    INIT_LSM6DSM,
#ifdef LSM6DSM_I2C_MASTER_ENABLED
    INIT_I2C_MASTER_REGS_CONF,
    INIT_I2C_MASTER_SENSOR_RESET,
    INIT_I2C_MASTER_MAGN_SENSOR,
    INIT_I2C_MASTER_BARO_SENSOR,
    INIT_I2C_MASTER_SENSOR_END,
#endif /* LSM6DSM_I2C_MASTER_ENABLED */
    INIT_DONE,
};

enum SensorEvents {
    NO_EVT = -1,
    EVT_SPI_DONE = EVT_APP_START + 1,
    EVT_SENSOR_INTERRUPT_1
};

enum SensorState {
    SENSOR_BOOT = 0,
    SENSOR_VERIFY_WAI,
    SENSOR_INITIALIZATION,
    SENSOR_IDLE,
    SENSOR_POWERING_UP,
    SENSOR_POWERING_DOWN,
    SENSOR_CONFIG_CHANGING,
    SENSOR_INT1_STATUS_REG_HANDLING,
    SENSOR_INT1_OUTPUT_DATA_HANDLING
};

static void lsm6dsm_spiQueueRead(uint8_t addr, size_t size, uint8_t **buf, uint32_t delay);
static void lsm6dsm_spiQueueWrite(uint8_t addr, uint8_t data, uint32_t delay);
static void lsm6dsm_spiQueueMultiwrite(uint8_t addr, uint8_t *data, size_t size, uint32_t delay);

#define SPI_MULTIWRITE_0(addr, data, size)                         lsm6dsm_spiQueueMultiwrite(addr, data, size, 2)
#define SPI_MULTIWRITE_1(addr, data, size, delay)                  lsm6dsm_spiQueueMultiwrite(addr, data, size, delay)
#define GET_SPI_MULTIWRITE_MACRO(_1, _2, _3, _4, NAME, ...)        NAME
#define SPI_MULTIWRITE(...)                                        GET_SPI_MULTIWRITE_MACRO(__VA_ARGS__, SPI_MULTIWRITE_1, SPI_MULTIWRITE_0)(__VA_ARGS__)

#define SPI_WRITE_0(addr, data)                                    lsm6dsm_spiQueueWrite(addr, data, 2)
#define SPI_WRITE_1(addr, data, delay)                             lsm6dsm_spiQueueWrite(addr, data, delay)
#define GET_SPI_WRITE_MACRO(_1, _2, _3, NAME, ...)                 NAME
#define SPI_WRITE(...)                                             GET_SPI_WRITE_MACRO(__VA_ARGS__, SPI_WRITE_1, SPI_WRITE_0)(__VA_ARGS__)

#define SPI_READ_0(addr, size, buf)                                lsm6dsm_spiQueueRead(addr, size, buf, 0)
#define SPI_READ_1(addr, size, buf, delay)                         lsm6dsm_spiQueueRead(addr, size, buf, delay)
#define GET_SPI_READ_MACRO(_1, _2, _3, _4, NAME, ...)              NAME
#define SPI_READ(...)                                              GET_SPI_READ_MACRO(__VA_ARGS__, SPI_READ_1, SPI_READ_0)(__VA_ARGS__)

#ifdef LSM6DSM_I2C_MASTER_ENABLED
static void lsm6dsm_writeSlaveRegister(uint8_t addr, uint8_t value, uint32_t accelRate, uint32_t delay, enum SensorIndex si);

#define SPI_WRITE_SS_REGISTER_0(addr, value, accelRate, si)        lsm6dsm_writeSlaveRegister(addr, value, accelRate, 0, si)
#define SPI_WRITE_SS_REGISTER_1(addr, value, accelRate, si, delay) lsm6dsm_writeSlaveRegister(addr, value, accelRate, delay, si)
#define GET_SPI_WRITE_SS_MACRO(_1, _2, _3, _4, _5, NAME, ...)      NAME
#define SPI_WRITE_SLAVE_SENSOR_REGISTER(...)                       GET_SPI_WRITE_SS_MACRO(__VA_ARGS__, SPI_WRITE_SS_REGISTER_1, \
                                                                       SPI_WRITE_SS_REGISTER_0)(__VA_ARGS__)
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

#define INFO_PRINT(fmt, ...) \
    do { \
        osLog(LOG_INFO, "%s " fmt, "[LSM6DSM]", ##__VA_ARGS__); \
    } while (0);

#define DEBUG_PRINT(fmt, ...) \
    do { \
        if (LSM6DSM_DBG_ENABLED) { \
            osLog(LOG_DEBUG, "%s " fmt, "[LSM6DSM]", ##__VA_ARGS__); \
        } \
    } while (0);

#define ERROR_PRINT(fmt, ...) \
    do { \
        osLog(LOG_ERROR, "%s " fmt, "[LSM6DSM]", ##__VA_ARGS__); \
    } while (0);

/* DO NOT MODIFY, just to avoid compiler error if not defined using FLAGS */
#ifndef LSM6DSM_DBG_ENABLED
#define LSM6DSM_DBG_ENABLED                           0
#endif /* LSM6DSM_DBG_ENABLED */


/* struct LSM6DSMSPISlaveInterface: SPI slave data interface
 * @packets: spi packets to perform read/write operations.
 * @txrxBuffer: spi data buffer.
 * @spiDev: spi device pointer.
 * @mode: spi mode (frequency, polarity, etc).
 * @cs: chip select used by SPI slave.
 * @mWbufCnt: counter of total data in spi buffer.
 * @statusRegBuffer: pointer to txrxBuffer to access status register data.
 * @funcSrcBuffer: pointer to txrxBuffer to access func source register data.
 * @tmpDataBuffer: pointer to txrxBuffer to access sporadic read.
 * @accelDataBuffer: pointer to txrxBuffer to access accelerometer data.
 * @gyroDataBuffer: pointer to txrxBuffer to access gyroscope data.
 * @SHDataBuffer: pointer to txrxBuffer to access magnetometer data.
 * @stepCounterDataBuffer: pointer to txrxBuffer to access step counter data.
 * @tempCounterDataBuffer: pointer to txrxBuffer to access temperature data.
 * @mRegCnt: spi packet num counter.
 * @spiInUse: flag used to check if SPI is currently busy.
 */
struct LSM6DSMSPISlaveInterface {
    struct SpiPacket packets[LSM6DSM_SPI_PACKET_SIZE];
    uint8_t txrxBuffer[SPI_BUF_SIZE];
    struct SpiDevice *spiDev;
    struct SpiMode mode;
    spi_cs_t cs;
    uint16_t mWbufCnt;
    uint8_t *statusRegBuffer;
    uint8_t *funcSrcBuffer;
    uint8_t *tmpDataBuffer;
    uint8_t *accelDataBuffer;
    uint8_t *gyroDataBuffer;
#ifdef LSM6DSM_I2C_MASTER_ENABLED
    uint8_t *SHDataBuffer;
#endif /* LSM6DSM_I2C_MASTER_ENABLED */
    uint8_t *stepCounterDataBuffer;
#if defined(LSM6DSM_GYRO_CALIB_ENABLED) || defined(LSM6DSM_ACCEL_CALIB_ENABLED)
    uint8_t *tempDataBuffer;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED, LSM6DSM_ACCEL_CALIB_ENABLED */
    uint8_t mRegCnt;
    bool spiInUse;
};

/*
 * struct LSM6DSMConfigStatus: temporary data of pending events
 * @latency: value to be used in next setRate operation [ns].
 * @rate: value to be used in next setRate operation [Hz * 1024].
 * @enable: value to be used in next setEnable.
 */
struct LSM6DSMConfigStatus {
    uint64_t latency;
    uint32_t rate;
    bool enable;
};

/* struct LSM6DSMSensor: sensor status data
 * @pConfig: temporary data of pending events.
 * @tADataEvt: store three axis sensor data to send to nanohub.
 * @sADataEvt: store one axis sensor data to send to nanohub.
 * @latency: current value of latency [n].
 * @handle: sensor handle obtained by sensorRegister.
 * @rate: current value of rate [Hz * 1024].
 * @hwRate: current value of physical rate [Hz * 1024].
 * @idx: enum SensorIndex.
 * @samplesToDiscard: samples to discard after enable or odr switch.
 * @samplesDecimator: decimator factor to achieve lower odr not available in hw.
 * @samplesCounter: samples counter by decimation operation.
 * enabled: current status of sensor.
 */
struct LSM6DSMSensor {
    struct LSM6DSMConfigStatus pConfig;
    struct TripleAxisDataEvent *tADataEvt;
    struct SingleAxisDataEvent *sADataEvt;
    uint64_t latency;
    uint32_t handle;
    uint32_t rate;
    uint32_t hwRate;
    enum SensorIndex idx;
    uint8_t samplesToDiscard;
    uint8_t samplesDecimator;
    uint8_t samplesCounter;
    bool enabled;
};

/* struct LSM6DSMTask: task data
 * @sensors: sensor status data list.
 * @slaveConn: slave interface / communication data.
 * @accelCal: accelerometer calibration algo data.
 * @gyroCal: gyroscope calibration algo data.
 * @gmagnCal: magnetometer calibration algo data.
 * @int1: int1 gpio data.
 * @isr1: isr1 data.
 * @mDataSlabThreeAxis: sensors data memory three axis sensors.
 * @mDataSlabOneAxis: sensors data memory one axis sensors.
 * @currentTemperature: sensors temperature data value used by gyroscope/accelerometer bias calibration libs.
 * @timestampInt: timestamp value from isr.
 * @tid: task id.
 * @totalNumSteps: total number of steps of step counter sensor.
 * @triggerRate: max ODR value between accel or gyro.
 * @initState: initialization id done in several steps (enum InitState).
 * @state: task state, driver manage operations using a state machine (enum SensorState).
 * @mRetryLeft: counter used to retry operations #n times before return a failure.
 * @statusRegisterDA: acceleromter/gyroscope status register (data available).
 * @statusRegisterTDA: temperature status register (data available).
 * @statusRegisterSH: sensor-hub status register (slave data available).
 * @accelSensorDependencies: dependencies mask of sensors that are using accelerometer.
 * @embeddedFunctionsDependencies: dependencies mask of sensors that are using embedded functions.
 * @int1Register: interrupt 1 register status content (addr: 0x0d).
 * @int2Register: interrupt 2 register status content (addr: 0x0e).
 * @embeddedFunctionsRegister: embedded register status content (addr: 0x19).
 * @masterConfigRegister: i2c master register status content (addr: 0x1a).
 * @newMagnCalibData: this flag indicate if new magnetometer calibration data are available.
 * @readSteps: flag used to indicate if interrupt task need to read number of steps.
 * @pendingEnableConfig: pending setEnable operations to be executed.
 * @pendingRateConfig: pending setRate operations to be executed.
 * @pendingInt: pending interrupt task to be executed.
 */
typedef struct LSM6DSMTask {
    struct LSM6DSMSensor sensors[NUM_SENSORS];
    struct LSM6DSMSPISlaveInterface slaveConn;

#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
    struct accelCal_t accelCal;
    struct TripleAxisDataEvent *accelBiasDataEvt;
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    struct gyroCal_t gyroCal;
    struct TripleAxisDataEvent *gyroBiasDataEvt;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    struct MagCal magnCal;
    struct TripleAxisDataEvent *magnCalDataEvt;
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */

    struct Gpio *int1;
    struct ChainedIsr isr1;
    struct SlabAllocator *mDataSlabThreeAxis;
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    struct SlabAllocator *mDataSlabOneAxis;
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

#if defined(LSM6DSM_GYRO_CALIB_ENABLED) || defined(LSM6DSM_ACCEL_CALIB_ENABLED)
    float currentTemperature;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED, LSM6DSM_ACCEL_CALIB_ENABLED */

    uint64_t timestampInt[LSM6DSM_INT_NUM];

    uint32_t tid;
    uint32_t totalNumSteps;
    uint32_t triggerRate;

    enum InitState initState;
    volatile uint8_t state;

    uint8_t mRetryLeft;
    uint8_t statusRegisterDA;
#if defined(LSM6DSM_GYRO_CALIB_ENABLED) || defined(LSM6DSM_ACCEL_CALIB_ENABLED)
    uint8_t statusRegisterTDA;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED, LSM6DSM_ACCEL_CALIB_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_ENABLED
    uint8_t statusRegisterSH;
#endif /* LSM6DSM_I2C_MASTER_ENABLED */
    uint8_t accelSensorDependencies;
    uint8_t embeddedFunctionsDependencies;
    uint8_t int1Register;
    uint8_t int2Register;
    uint8_t embeddedFunctionsRegister;

#ifdef LSM6DSM_I2C_MASTER_ENABLED
    uint8_t masterConfigRegister;
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    bool newMagnCalibData;
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */

    bool readSteps;
    bool pendingEnableConfig[NUM_SENSORS];
    bool pendingRateConfig[NUM_SENSORS];
    bool pendingInt[LSM6DSM_INT_NUM];
} LSM6DSMTask;

static LSM6DSMTask mTask;

#define TASK                                          LSM6DSMTask* const _task
#define TDECL()                                       TASK = &mTask; (void)_task
#define T(v)                                          (_task->v)
#define T_SLAVE_INTERFACE(v)                          (_task->slaveConn.v)

#define BIT(x)                                        (0x01 << x)
#define SENSOR_HZ_RATE_TO_US(x)                       (1024000000UL / x)
#define NS_TO_US(ns)                                  cpuMathU64DivByU16(ns, 1000)

/* Atomic get state */
#define GET_STATE()                                   (atomicReadByte(&(_task->state)))

/* Atomic set state, this set the state to arbitrary value, use with caution */
#define SET_STATE(s) \
    do { \
        atomicWriteByte(&(_task->state), (s)); \
    } while (0)

static bool trySwitchState_(TASK, enum SensorState newState)
{
    return atomicCmpXchgByte(&T(state), SENSOR_IDLE, newState);
}
#define trySwitchState(s) trySwitchState_(_task, (s))

static void lsm6dsm_readStatusReg_(TASK, bool isInterruptContext);
#define lsm6dsm_readStatusReg(a)                      lsm6dsm_readStatusReg_(_task, (a))

#define DEC_INFO(name, type, axis, inter, samples) \
    .sensorName = name, \
    .sensorType = type, \
    .numAxis = axis, \
    .interrupt = inter, \
    .minSamples = samples

#define DEC_INFO_RATE(name, rates, type, axis, inter, samples) \
    DEC_INFO(name, type, axis, inter, samples), \
    .supportedRates = rates

#define DEC_INFO_RATE_BIAS(name, rates, type, axis, inter, samples, bias) \
    DEC_INFO(name, type, axis, inter, samples), \
    .supportedRates = rates, \
    .flags1 = SENSOR_INFO_FLAGS1_BIAS, \
    .biasType = bias

#define DEC_INFO_RATE_RAW(name, rates, type, axis, inter, samples, raw, scale) \
    DEC_INFO(name, type, axis, inter, samples), \
    .supportedRates = rates, \
    .flags1 = SENSOR_INFO_FLAGS1_RAW, \
    .rawType = raw, \
    .rawScale = scale

#define DEC_INFO_RATE_RAW_BIAS(name, rates, type, axis, inter, samples, raw, scale, bias) \
    DEC_INFO_RATE_RAW(name, rates, type, axis, inter, samples, raw, scale), \
    .flags1 = SENSOR_INFO_FLAGS1_RAW | SENSOR_INFO_FLAGS1_BIAS, \
    .biasType = bias

/* LSM6DSMImuRates: supported frequencies by accelerometer and gyroscope sensors
 * LSM6DSMImuRatesRegValue, LSM6DSMRatesSamplesToDiscardGyroPowerOn, LSM6DSMAccelRatesSamplesToDiscard,
 *     LSM6DSMGyroRatesSamplesToDiscard must have same length.
 */
static uint32_t LSM6DSMImuRates[] = {
    SENSOR_HZ(26.0f / 8.0f),      /* 3.25Hz */
    SENSOR_HZ(26.0f / 4.0f),      /* 6.5Hz */
    SENSOR_HZ(26.0f / 2.0f),      /* 12.5Hz */
    SENSOR_HZ(26.0f),             /* 26Hz */
    SENSOR_HZ(52.0f),             /* 52Hz */
    SENSOR_HZ(104.0f),            /* 104Hz */
    SENSOR_HZ(208.0f),            /* 208Hz */
    SENSOR_HZ(416.0f),            /* 416Hz */
    0,
};

static uint8_t LSM6DSMImuRatesRegValue[] = {
    LSM6DSM_ODR_12HZ_REG_VALUE,   /* 3.25Hz - do not exist, use 12.5Hz */
    LSM6DSM_ODR_12HZ_REG_VALUE,   /* 6.5Hz - do not exist, use 12.5Hz */
    LSM6DSM_ODR_12HZ_REG_VALUE,   /* 12.5Hz */
    LSM6DSM_ODR_26HZ_REG_VALUE,   /* 26Hz */
    LSM6DSM_ODR_52HZ_REG_VALUE,   /* 52Hz */
    LSM6DSM_ODR_104HZ_REG_VALUE,  /* 104Hz */
    LSM6DSM_ODR_208HZ_REG_VALUE,  /* 208Hz */
    LSM6DSM_ODR_416HZ_REG_VALUE,  /* 416Hz */
};

/* When sensors switch status from power-down, constant boottime must be considered, some samples should be discarded */
static uint8_t LSM6DSMRatesSamplesToDiscardGyroPowerOn[] = {
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 80000, /* 3.25Hz - do not exist, use 12.5Hz = 80000us */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 80000, /* 6.5Hz - do not exist, use 12.5Hz = 80000us */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 80000, /* 12.5Hz = 80000us */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 38461, /* 26Hz = 38461us */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 19230, /* 52Hz = 19230s */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 9615,  /* 104Hz = 9615us */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 4807,  /* 208Hz = 4807us */
    LSM6DSM_ODR_DELAY_US_GYRO_POWER_ON / 2403,  /* 416Hz = 2403us */
};

/* When accelerometer change odr but sensor is already on, few samples should be discarded */
static uint8_t LSM6DSMAccelRatesSamplesToDiscard[] = {
    LSM6DSM_ODR_12HZ_ACCEL_STD,   /* 3.25Hz - do not exist, use 12.5Hz */
    LSM6DSM_ODR_12HZ_ACCEL_STD,   /* 6.5Hz - do not exist, use 12.5Hz */
    LSM6DSM_ODR_12HZ_ACCEL_STD,   /* 12.5Hz */
    LSM6DSM_ODR_26HZ_ACCEL_STD,   /* 26Hz */
    LSM6DSM_ODR_52HZ_ACCEL_STD,   /* 52Hz */
    LSM6DSM_ODR_104HZ_ACCEL_STD,  /* 104Hz */
    LSM6DSM_ODR_208HZ_ACCEL_STD,  /* 208Hz */
    LSM6DSM_ODR_416HZ_ACCEL_STD,  /* 416Hz */
};

/* When gyroscope change odr but sensor is already on, few samples should be discarded */
static uint8_t LSM6DSMGyroRatesSamplesToDiscard[] = {
    LSM6DSM_ODR_12HZ_GYRO_STD,    /* 3.25Hz - do not exist, use 12.5Hz */
    LSM6DSM_ODR_12HZ_GYRO_STD,    /* 6.5Hz - do not exist, use 12.5Hz */
    LSM6DSM_ODR_12HZ_GYRO_STD,    /* 12.5Hz */
    LSM6DSM_ODR_26HZ_GYRO_STD,    /* 26Hz */
    LSM6DSM_ODR_52HZ_GYRO_STD,    /* 52Hz */
    LSM6DSM_ODR_104HZ_GYRO_STD,   /* 104Hz */
    LSM6DSM_ODR_208HZ_GYRO_STD,   /* 208Hz */
    LSM6DSM_ODR_416HZ_GYRO_STD,   /* 416Hz */
};

#ifdef LSM6DSM_I2C_MASTER_ENABLED
static uint32_t LSM6DSMSHRates[] = {
    SENSOR_HZ(26.0f / 8.0f),      /* 3.25Hz */
    SENSOR_HZ(26.0f / 4.0f),      /* 6.5Hz */
    SENSOR_HZ(26.0f / 2.0f),      /* 12.5Hz */
    SENSOR_HZ(26.0f),             /* 26Hz */
    SENSOR_HZ(52.0f),             /* 52Hz */
    SENSOR_HZ(104.0f),            /* 104Hz */
    0,
};
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

#define LSM6DSM_SC_DELTA_TIME_PERIOD_SEC              (1.6384f)

static uint32_t LSM6DSMStepCounterRates[] = {
    SENSOR_HZ(1.0f / (128 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)), /* 209.715 sec */
    SENSOR_HZ(1.0f / (64 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),  /* 104.857 sec */
    SENSOR_HZ(1.0f / (32 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),  /* 52.4288 sec */
    SENSOR_HZ(1.0f / (16 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),  /* 26.1574 sec */
    SENSOR_HZ(1.0f / (8 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),   /* 13.0787 sec */
    SENSOR_HZ(1.0f / (4 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),   /* 6.53936 sec */
    SENSOR_HZ(1.0f / (2 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),   /* 3.26968 sec */
    SENSOR_HZ(1.0f / (1 * LSM6DSM_SC_DELTA_TIME_PERIOD_SEC)),   /* 1.63840 sec */
    SENSOR_RATE_ONCHANGE,
    0,
};

static const struct SensorInfo LSM6DSMSensorInfo[NUM_SENSORS] = {
    {
#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
        DEC_INFO_RATE_RAW_BIAS("Accelerometer", LSM6DSMImuRates, SENS_TYPE_ACCEL, NUM_AXIS_THREE,
            NANOHUB_INT_NONWAKEUP, 1, SENS_TYPE_ACCEL_RAW, 1.0f / LSM6DSM_ACCEL_KSCALE, SENS_TYPE_ACCEL_BIAS)
#else /* LSM6DSM_ACCEL_CALIB_ENABLED */
        DEC_INFO_RATE_RAW("Accelerometer", LSM6DSMImuRates, SENS_TYPE_ACCEL, NUM_AXIS_THREE,
            NANOHUB_INT_NONWAKEUP, 1, SENS_TYPE_ACCEL_RAW, 1.0f / LSM6DSM_ACCEL_KSCALE)
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */
    },
    {
#ifdef LSM6DSM_GYRO_CALIB_ENABLED
        DEC_INFO_RATE_BIAS("Gyroscope", LSM6DSMImuRates, SENS_TYPE_GYRO, NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 1, SENS_TYPE_GYRO_BIAS)
#else /* LSM6DSM_GYRO_CALIB_ENABLED */
        DEC_INFO_RATE("Gyroscope", LSM6DSMImuRates, SENS_TYPE_GYRO, NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 1)
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
    },
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    {
#ifdef LSM6DSM_MAGN_CALIB_ENABLED
        DEC_INFO_RATE_BIAS("Magnetometer", LSM6DSMSHRates, SENS_TYPE_MAG, NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 1, SENS_TYPE_MAG_BIAS)
#else /* LSM6DSM_MAGN_CALIB_ENABLED */
        DEC_INFO_RATE("Magnetometer", LSM6DSMSHRates, SENS_TYPE_MAG, NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 1)
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */
    },
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    {
        DEC_INFO_RATE("Pressure", LSM6DSMSHRates, SENS_TYPE_BARO, NUM_AXIS_ONE, NANOHUB_INT_NONWAKEUP, 1)
    },
    {
        DEC_INFO_RATE("Temperature", LSM6DSMSHRates, SENS_TYPE_TEMP, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, 1)
    },
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
    {
        DEC_INFO("Step Detector", SENS_TYPE_STEP_DETECT, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, 1)
    },
    {
        DEC_INFO_RATE("Step Counter", LSM6DSMStepCounterRates, SENS_TYPE_STEP_COUNT, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, 1)
    },
    {
        DEC_INFO("Significant Motion", SENS_TYPE_SIG_MOTION, NUM_AXIS_EMBEDDED, NANOHUB_INT_WAKEUP, 1)
    },
};

#define DEC_OPS(power, firmware, rate, flush) \
    .sensorPower = power, \
    .sensorFirmwareUpload = firmware, \
    .sensorSetRate = rate, \
    .sensorFlush = flush

#define DEC_OPS_SEND(power, firmware, rate, flush, send) \
    .sensorPower = power, \
    .sensorFirmwareUpload = firmware, \
    .sensorSetRate = rate, \
    .sensorFlush = flush, \
    .sensorSendOneDirectEvt = send

static bool lsm6dsm_setAccelPower(bool on, void *cookie);
static bool lsm6dsm_setGyroPower(bool on, void *cookie);
static bool lsm6dsm_setStepDetectorPower(bool on, void *cookie);
static bool lsm6dsm_setStepCounterPower(bool on, void *cookie);
static bool lsm6dsm_setSignMotionPower(bool on, void *cookie);
static bool lsm6dsm_accelFirmwareUpload(void *cookie);
static bool lsm6dsm_gyroFirmwareUpload(void *cookie);
static bool lsm6dsm_stepDetectorFirmwareUpload(void *cookie);
static bool lsm6dsm_stepCounterFirmwareUpload(void *cookie);
static bool lsm6dsm_signMotionFirmwareUpload(void *cookie);
static bool lsm6dsm_setAccelRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_setGyroRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_setStepDetectorRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_setStepCounterRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_setSignMotionRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_accelFlush(void *cookie);
static bool lsm6dsm_gyroFlush(void *cookie);
static bool lsm6dsm_stepDetectorFlush(void *cookie);
static bool lsm6dsm_stepCounterFlush(void *cookie);
static bool lsm6dsm_signMotionFlush(void *cookie);
static bool lsm6dsm_stepCounterSendLastData(void *cookie, uint32_t tid);

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
static bool lsm6dsm_setMagnPower(bool on, void *cookie);
static bool lsm6dsm_magnFirmwareUpload(void *cookie);
static bool lsm6dsm_setMagnRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_magnFlush(void *cookie);
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
static bool lsm6dsm_setPressPower(bool on, void *cookie);
static bool lsm6dsm_pressFirmwareUpload(void *cookie);
static bool lsm6dsm_setPressRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_pressFlush(void *cookie);
static bool lsm6dsm_setTempPower(bool on, void *cookie);
static bool lsm6dsm_tempFirmwareUpload(void *cookie);
static bool lsm6dsm_setTempRate(uint32_t rate, uint64_t latency, void *cookie);
static bool lsm6dsm_tempFlush(void *cookie);
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

static const struct SensorOps LSM6DSMSensorOps[NUM_SENSORS] = {
    { DEC_OPS(lsm6dsm_setAccelPower, lsm6dsm_accelFirmwareUpload, lsm6dsm_setAccelRate, lsm6dsm_accelFlush) },
    { DEC_OPS(lsm6dsm_setGyroPower, lsm6dsm_gyroFirmwareUpload, lsm6dsm_setGyroRate, lsm6dsm_gyroFlush) },
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    { DEC_OPS(lsm6dsm_setMagnPower, lsm6dsm_magnFirmwareUpload, lsm6dsm_setMagnRate, lsm6dsm_magnFlush) },
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    { DEC_OPS(lsm6dsm_setPressPower, lsm6dsm_pressFirmwareUpload, lsm6dsm_setPressRate, lsm6dsm_pressFlush) },
    { DEC_OPS(lsm6dsm_setTempPower, lsm6dsm_tempFirmwareUpload, lsm6dsm_setTempRate, lsm6dsm_tempFlush) },
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
    { DEC_OPS(lsm6dsm_setStepDetectorPower, lsm6dsm_stepDetectorFirmwareUpload, lsm6dsm_setStepDetectorRate, lsm6dsm_stepDetectorFlush) },
    { DEC_OPS_SEND(lsm6dsm_setStepCounterPower, lsm6dsm_stepCounterFirmwareUpload, lsm6dsm_setStepCounterRate, lsm6dsm_stepCounterFlush, lsm6dsm_stepCounterSendLastData) },
    { DEC_OPS(lsm6dsm_setSignMotionPower, lsm6dsm_signMotionFirmwareUpload, lsm6dsm_setSignMotionRate, lsm6dsm_signMotionFlush) },
};

static void lsm6dsm_spiQueueRead(uint8_t addr, size_t size, uint8_t **buf, uint32_t delay)
{
    TDECL();

    if (T_SLAVE_INTERFACE(spiInUse)) {
        ERROR_PRINT("SPI in use, cannot queue read (addr=%d len=%d)\n", (int)addr, (int)size);
        return;
    }

    *buf = &T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)]);

    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).size = size + 1;
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).txBuf = &T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)]);
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).rxBuf = *buf;
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).delay = delay * 1000;

    T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)++]) = addr | 0x80;
    T_SLAVE_INTERFACE(mWbufCnt) += size;
    T_SLAVE_INTERFACE(mRegCnt)++;
}

static void lsm6dsm_spiQueueWrite(uint8_t addr, uint8_t data, uint32_t delay)
{
    TDECL();

    if (T_SLAVE_INTERFACE(spiInUse)) {
        ERROR_PRINT("SPI in use, cannot queue write (addr=%d data=%d)\n", (int)addr, (int)data);
        return;
    }

    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).size = 2;
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).txBuf = &T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)]);
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).rxBuf = &T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)]);
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).delay = delay * 1000;

    T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)++]) = addr;
    T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)++]) = data;
    T_SLAVE_INTERFACE(mRegCnt)++;
}

static void lsm6dsm_spiQueueMultiwrite(uint8_t addr, uint8_t *data, size_t size, uint32_t delay)
{
    TDECL();
    uint8_t i;

    if (T_SLAVE_INTERFACE(spiInUse)) {
        ERROR_PRINT("SPI in use, cannot queue multiwrite (addr=%d size=%d)\n", (int)addr, (int)size);
        return;
    }

    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).size = 1 + size;
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).txBuf = &T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)]);
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).rxBuf = &T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)]);
    T_SLAVE_INTERFACE(packets[T_SLAVE_INTERFACE(mRegCnt)]).delay = delay * 1000;

    T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)++]) = addr;

    for (i = 0; i < size; i++)
        T_SLAVE_INTERFACE(txrxBuffer[T_SLAVE_INTERFACE(mWbufCnt)++]) = data[i];

    T_SLAVE_INTERFACE(mRegCnt)++;
}

static void lsm6dsm_spiBatchTxRx(struct SpiMode *mode,
            SpiCbkF callback, void *cookie, const char *src)
{
    TDECL();
    uint8_t regCount;

    if (T_SLAVE_INTERFACE(mWbufCnt) > SPI_BUF_SIZE) {
        ERROR_PRINT("No enough SPI buffer space, dropping transaction\n");
        return;
    }

    if (T_SLAVE_INTERFACE(mRegCnt) > LSM6DSM_SPI_PACKET_SIZE) {
        ERROR_PRINT("spiBatchTxRx too many packets!\n");
        return;
    }

    /* Reset variables before issuing SPI transaction.
       SPI may finish before spiMasterRxTx finish */
    regCount = T_SLAVE_INTERFACE(mRegCnt);
    T_SLAVE_INTERFACE(spiInUse) = true;
    T_SLAVE_INTERFACE(mRegCnt) = 0;
    T_SLAVE_INTERFACE(mWbufCnt) = 0;

    if (spiMasterRxTx(T_SLAVE_INTERFACE(spiDev), T_SLAVE_INTERFACE(cs), T_SLAVE_INTERFACE(packets), regCount, mode, callback, cookie)) {
        ERROR_PRINT("spiBatchTxRx failed!\n");
    }
}

static void lsm6dsm_timerCallback(uint32_t timerId, void *data)
{
    osEnqueuePrivateEvt(EVT_SPI_DONE, data, NULL, mTask.tid);
}

static void lsm6dsm_spiCallback(void *cookie, int err)
{
    TDECL();

    T_SLAVE_INTERFACE(spiInUse) = false;
    osEnqueuePrivateEvt(EVT_SPI_DONE, cookie, NULL, mTask.tid);
}

static void lsm6dsm_readStatusReg_(TASK, bool isInterruptContext)
{
    if (trySwitchState(SENSOR_INT1_STATUS_REG_HANDLING)) {
        SPI_READ(LSM6DSM_STATUS_REG_ADDR, 1, &T_SLAVE_INTERFACE(statusRegBuffer));
        SPI_READ(LSM6DSM_FUNC_SRC_ADDR, 1, &T_SLAVE_INTERFACE(funcSrcBuffer));
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
    } else {
        if (isInterruptContext)
            osEnqueuePrivateEvt(EVT_SENSOR_INTERRUPT_1, _task, NULL, T(tid));
        else
            T(pendingInt[LSM6DSM_INT1_INDEX]) = true;
    }
}

/*
 * lsm6dsm_isr1: INT-1 line service routine
 */
static bool lsm6dsm_isr1(struct ChainedIsr *isr)
{
    TDECL();

    if (!extiIsPendingGpio(T(int1)))
        return false;

    T(timestampInt[LSM6DSM_INT1_INDEX]) = rtcGetTime();
    lsm6dsm_readStatusReg(true);

    extiClearPendingGpio(T(int1));

    return true;
}

/*
 * lsm6dsm_enableInterrupt: enable driver interrupt capability
 */
static void lsm6dsm_enableInterrupt(struct Gpio *pin, struct ChainedIsr *isr)
{
    gpioConfigInput(pin, GPIO_SPEED_LOW, GPIO_PULL_NONE);
    syscfgSetExtiPort(pin);
    extiEnableIntGpio(pin, EXTI_TRIGGER_RISING);
    extiChainIsr(LSM6DSM_INT_IRQ, isr);
}

/*
 * lsm6dsm_disableInterrupt: disable driver interrupt capability
 */
static void lsm6dsm_disableInterrupt(struct Gpio *pin, struct ChainedIsr *isr)
{
    extiUnchainIsr(LSM6DSM_INT_IRQ, isr);
    extiDisableIntGpio(pin);
}

/*
 * lsm6dsm_writeEmbeddedRegister: write embedded register of sensor
 */
static void lsm6dsm_writeEmbeddedRegister(uint8_t addr, uint8_t value)
{
    TDECL();

    SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister) & ~LSM6DSM_ENABLE_DIGITAL_FUNC, 3000);
    SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE | LSM6DSM_ENABLE_FUNC_CFG_ACCESS, 50);

    SPI_WRITE(addr, value);

    SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE, 50);
    SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
}

#ifdef LSM6DSM_I2C_MASTER_ENABLED
/*
 * lsm6dsm_writeSlaveRegister: write I2C slave register using sensor-hub feature
 */
static void lsm6dsm_writeSlaveRegister(uint8_t addr, uint8_t value, uint32_t accelRate, uint32_t delay, enum SensorIndex si)
{
    TDECL();
    uint8_t slave_addr, buffer[3];
    uint32_t sh_op_complete_time;

    switch (si) {
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    case MAGN:
        slave_addr = LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT;
        break;
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    case PRESS:
    case TEMP:
        slave_addr = LSM6DSM_SENSOR_SLAVE_BARO_I2C_ADDR_8BIT;
        break;
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
    default:
        return;
    }

    if (accelRate > SENSOR_HZ(104.0f))
        sh_op_complete_time = SENSOR_HZ_RATE_TO_US(SENSOR_HZ(104.0f));
    else
        sh_op_complete_time = SENSOR_HZ_RATE_TO_US(accelRate);

    /* Perform write to slave sensor and wait write is done (1 accel ODR) */
    SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister) & ~LSM6DSM_ENABLE_DIGITAL_FUNC, 3000);
    SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE | LSM6DSM_ENABLE_FUNC_CFG_ACCESS, 50);

    buffer[0] = slave_addr << 1;                                     /* LSM6DSM_EMBEDDED_SLV0_ADDR */
    buffer[1] = addr;                                                /* LSM6DSM_EMBEDDED_SLV0_SUBADDR */
    buffer[2] = LSM6DSM_EMBEDDED_SENSOR_HUB_HAVE_ONLY_WRITE;         /* LSM6DSM_EMBEDDED_SLV0_CONFIG */
    SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV0_ADDR_ADDR, buffer, 3);
    SPI_WRITE(LSM6DSM_EMBEDDED_DATAWRITE_SLV0_ADDR, value);

    SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE, 50);
    SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister), (3 * sh_op_complete_time) / 2);

    /* After write is completed slave 0 must be set to sleep mode */
    SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister) & ~LSM6DSM_ENABLE_DIGITAL_FUNC, 3000);
    SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE | LSM6DSM_ENABLE_FUNC_CFG_ACCESS, 50);

    buffer[0] = LSM6DSM_EMBEDDED_SLV0_WRITE_ADDR_SLEEP;              /* LSM6DSM_EMBEDDED_SLV0_ADDR */
    buffer[1] = addr;                                                /* LSM6DSM_EMBEDDED_SLV0_SUBADDR */
    buffer[2] = LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE;               /* LSM6DSM_EMBEDDED_SLV0_CONFIG */
    SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV0_ADDR_ADDR, buffer, 3);

    SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE, 50);
    SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
}
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

/*
 * lsm6dsm_computeOdr: get index of LSM6DSMImuRates array based on selected rate
 */
static uint8_t lsm6dsm_computeOdr(uint32_t rate)
{
    int i;

    for (i = 0; i < (ARRAY_SIZE(LSM6DSMImuRates) - 1); i++) {
        if (LSM6DSMImuRates[i] == rate)
            break;
    }
    if (i == (ARRAY_SIZE(LSM6DSMImuRates) -1 )) {
        ERROR_PRINT("ODR not valid! Selected smallest ODR available\n");
        i = 0;
    }

    return i;
}

/*
 * lsm6dsm_getAccelHwMinOdr: verify minimum odr needed by accel in order to satisfy dependencies
 */
static uint8_t lsm6dsm_getAccelHwMinOdr()
{
    TDECL();
    uint32_t minRate = SENSOR_HZ(26.0f / 2.0f);

    if (T(accelSensorDependencies) & BIT(ACCEL)) {
        if (minRate < T(sensors[ACCEL]).rate)
            minRate = T(sensors[ACCEL]).rate;
    }

    /* Embedded functions are enabled, min odr required is 26Hz */
    if (T(embeddedFunctionsDependencies)) {
        if (minRate < SENSOR_HZ(26.0f))
            minRate = SENSOR_HZ(26.0f);
    }

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    if (T(accelSensorDependencies) & BIT(GYRO)) {
        if (minRate < T(sensors[GYRO].rate))
            minRate = T(sensors[GYRO].rate);
    }
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    if (T(accelSensorDependencies) & BIT(MAGN)) {
        if (minRate < T(sensors[MAGN].rate))
            minRate = T(sensors[MAGN].rate);
    }
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    if (T(accelSensorDependencies) & BIT(PRESS)) {
        if (minRate < T(sensors[PRESS].rate))
            minRate = T(sensors[PRESS].rate);
    }

    if (T(accelSensorDependencies) & BIT(TEMP)) {
        if (minRate < T(sensors[TEMP].rate))
            minRate = T(sensors[TEMP].rate);
    }
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

    /* This call return index of LSM6DSMImuRates struct element */
    return lsm6dsm_computeOdr(minRate);
}

/*
 * lsm6dsm_setTriggerRate: detect between accel & gyro fastest odr
 */
static void lsm6dsm_setTriggerRate()
{
    TDECL();
    uint8_t i;

    i = lsm6dsm_getAccelHwMinOdr();

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    T(triggerRate) = SENSOR_HZ_RATE_TO_US(LSM6DSMImuRates[i]);
#else /* LSM6DSM_GYRO_CALIB_ENABLED */
    uint32_t maxRate = LSM6DSMImuRates[i];

    if (maxRate < T(sensors[GYRO]).hwRate)
        maxRate = T(sensors[GYRO]).hwRate;

    T(triggerRate) = SENSOR_HZ_RATE_TO_US(maxRate);
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
}

/*
 * lsm6dsm_updateAccelOdr: update accel odr based on enabled dependencies
 */
static bool lsm6dsm_updateAccelOdr()
{
    TDECL();
    uint8_t i;

    /* If no one is using accel disable it. If dependencies are using accel try to reduce ODR */
    if (T(accelSensorDependencies) == 0) {
        DEBUG_PRINT("updateAccelOdr: no one is using accel, disabling it\n");
        T(sensors[ACCEL]).hwRate = 0;
        SPI_WRITE(LSM6DSM_CTRL1_XL_ADDR, LSM6DSM_CTRL1_XL_BASE);
        lsm6dsm_setTriggerRate();
    } else {
        i = lsm6dsm_getAccelHwMinOdr();

        T(sensors[ACCEL]).samplesDecimator = LSM6DSMImuRates[i] / T(sensors[ACCEL]).rate;
        T(sensors[ACCEL]).samplesCounter = T(sensors[ACCEL]).samplesDecimator - 1;
        T(sensors[ACCEL]).samplesToDiscard = LSM6DSMAccelRatesSamplesToDiscard[i];
        T(sensors[ACCEL]).hwRate = LSM6DSMImuRates[i];

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
        if (T(accelSensorDependencies) & BIT(MAGN)) {
            if (T(sensors[ACCEL]).hwRate > SENSOR_HZ(104.0f))
                T(sensors[MAGN]).samplesDecimator = SENSOR_HZ(104.0f) / T(sensors[MAGN]).rate;
            else
                T(sensors[MAGN]).samplesDecimator = T(sensors[ACCEL]).hwRate / T(sensors[MAGN]).rate;

            T(sensors[MAGN]).samplesCounter = T(sensors[MAGN]).samplesDecimator - 1;
            T(sensors[MAGN]).samplesToDiscard = 1;
        }
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        if (T(accelSensorDependencies) & BIT(PRESS)) {
            if (T(sensors[ACCEL]).hwRate > SENSOR_HZ(104.0f))
                T(sensors[PRESS]).samplesDecimator = SENSOR_HZ(104.0f) / T(sensors[PRESS]).rate;
            else
                T(sensors[PRESS]).samplesDecimator = T(sensors[ACCEL]).hwRate / T(sensors[PRESS]).rate;

            T(sensors[PRESS]).samplesCounter = T(sensors[PRESS]).samplesDecimator - 1;
            T(sensors[PRESS]).samplesToDiscard = 1;
        }

        if (T(accelSensorDependencies) & BIT(TEMP)) {
            if (T(sensors[ACCEL]).hwRate > SENSOR_HZ(104.0f))
                T(sensors[TEMP]).samplesDecimator = SENSOR_HZ(104.0f) / T(sensors[TEMP]).rate;
            else
                T(sensors[TEMP]).samplesDecimator = T(sensors[ACCEL]).hwRate / T(sensors[TEMP]).rate;

            T(sensors[TEMP]).samplesCounter = T(sensors[TEMP]).samplesDecimator - 1;
            T(sensors[TEMP]).samplesToDiscard = 1;
        }
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

        lsm6dsm_setTriggerRate();

        DEBUG_PRINT("updateAccelOdr: accel in use, updating odr to %dHz\n", (int)(T(sensors[ACCEL]).hwRate / 1024));

        SPI_WRITE(LSM6DSM_CTRL1_XL_ADDR, LSM6DSM_CTRL1_XL_BASE | LSM6DSMImuRatesRegValue[i]);
    }

    return true;
}

/*
 * lsm6dsm_setAccelPower: enable/disable accelerometer sensor
 */
static bool lsm6dsm_setAccelPower(bool on, void *cookie)
{
    TDECL();

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setAccelPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(accelSensorDependencies) |= BIT(ACCEL);
            T(int1Register) |= LSM6DSM_INT_ACCEL_ENABLE_REG_VALUE;

            /* Discard same samples if interrupts are generated during INT enable */
            T(sensors[ACCEL]).samplesToDiscard = 255;

            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
        } else {
            T(accelSensorDependencies) &= ~BIT(ACCEL);
            T(int1Register) &= ~LSM6DSM_INT_ACCEL_ENABLE_REG_VALUE;

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
        }

        /* If enable, set only INT bit enable (sensor will be switched on by setRate function). If disable, it depends on accelSensorDependencies */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[ACCEL]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[ACCEL]) = true;
        T(sensors[ACCEL]).pConfig.enable = on;
    }

    return true;
}

/*
 * lsm6dsm_setGyroPower: enable/disable gyroscope sensor
 */
static bool lsm6dsm_setGyroPower(bool on, void *cookie)
{
    TDECL();

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setGyroPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(int1Register) |= LSM6DSM_INT_GYRO_ENABLE_REG_VALUE;

            /* Discard same samples if interrupts are generated during INT enable */
            T(sensors[GYRO]).samplesToDiscard = 255;

            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
        } else {
            T(int1Register) &= ~LSM6DSM_INT_GYRO_ENABLE_REG_VALUE;

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
            T(accelSensorDependencies) &= ~BIT(GYRO);

            if (!T(sensors[ACCEL].enabled))
                T(int1Register) &= ~LSM6DSM_INT_ACCEL_ENABLE_REG_VALUE;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

            T(sensors[GYRO]).hwRate = 0;

            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
            SPI_WRITE(LSM6DSM_CTRL2_G_ADDR, LSM6DSM_CTRL2_G_BASE);

            lsm6dsm_updateAccelOdr();
        }

        /* If enable, set only INT bit enable (sensor will be switched on by setRate function). If disable, disable INT bit and disable ODR (power-off sensor) */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[GYRO]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[GYRO]) = true;
        T(sensors[GYRO]).pConfig.enable = on;
    }

    return true;
}

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
/*
 * lsm6dsm_setMagnPower: enable/disable magnetometer sensor
 */
static bool lsm6dsm_setMagnPower(bool on, void *cookie)
{
    TDECL();

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setMagnPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(masterConfigRegister) |= LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

            /* Discard same samples if interrupts are generated during INT enable before switch on sensor */
            T(sensors[MAGN]).samplesToDiscard = 255;

            SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
        } else {
            T(accelSensorDependencies) &= ~BIT(MAGN);
            T(embeddedFunctionsDependencies) &= ~BIT(MAGN);

            SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ADDR,
                    LSM6DSM_SENSOR_SLAVE_MAGN_POWER_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE,
                    T(sensors[ACCEL]).hwRate, MAGN);

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
            if (!(T(sensors[PRESS].enabled) || T(sensors[TEMP].enabled))) {
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_MASTER_ON;
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

                SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
            }
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

            if (T(embeddedFunctionsDependencies) == 0) {
                DEBUG_PRINT("setMagnPower: no embedded sensors on, disabling digital functions\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_DIGITAL_FUNC;
                SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
            }

            T(sensors[MAGN]).hwRate = 0;

            lsm6dsm_updateAccelOdr();
        }

        /* If enable, set only INT bit enable (sensor will be switched on by setRate function).
           If disable, disable INT bit, disable sensor-hub and disable ODR (power-off sensor) */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[MAGN]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[MAGN]) = true;
        T(sensors[MAGN]).pConfig.enable = on;
    }

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
/*
 * lsm6dsm_setPressPower: enable/disable pressure sensor
 */
static bool lsm6dsm_setPressPower(bool on, void *cookie)
{
    TDECL();
    uint8_t i, reg_value = LSM6DSM_SENSOR_SLAVE_BARO_POWER_BASE;

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setPressPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(masterConfigRegister) |= LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

            /* Discard same samples if interrupts are generated during INT enable before switch on sensor */
            T(sensors[PRESS]).samplesToDiscard = 255;

            SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
        } else {
            T(accelSensorDependencies) &= ~BIT(PRESS);
            T(embeddedFunctionsDependencies) &= ~BIT(PRESS);

            if (T(sensors[TEMP]).enabled) {
                i = lsm6dsm_computeOdr(T(sensors[TEMP]).rate);
                reg_value |= LSM6DSM_SENSOR_SLAVE_BARO_RATES_REG_VALUE(i);
            } else
                reg_value |= LSM6DSM_SENSOR_SLAVE_BARO_POWER_OFF_VALUE;

            SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_BARO_POWER_ADDR, reg_value,
                    T(sensors[PRESS]).hwRate, PRESS);

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
            if (!(T(sensors[MAGN].enabled) || T(sensors[TEMP].enabled))) {
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_MASTER_ON;
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

                SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
            }
#else /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
            if (!T(sensors[TEMP].enabled)) {
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_MASTER_ON;
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

                SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
            }
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

            if (T(embeddedFunctionsDependencies) == 0) {
                DEBUG_PRINT("setPressPower: no embedded sensors on, disabling digital functions\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_DIGITAL_FUNC;
                SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
            }

            T(sensors[PRESS]).hwRate = 0;

            lsm6dsm_updateAccelOdr();
        }

        /* If enable, set only INT bit enable (sensor will be switched on by setRate function).
           If disable, disable INT bit, disable sensor-hub and disable ODR (power-off sensor) */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[PRESS]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[PRESS]) = true;
        T(sensors[PRESS]).pConfig.enable = on;
    }

    return true;
}

/*
 * lsm6dsm_setTempPower: enable/disable temperature sensor
 */
static bool lsm6dsm_setTempPower(bool on, void *cookie)
{
    TDECL();
    uint8_t i, reg_value = LSM6DSM_SENSOR_SLAVE_BARO_POWER_BASE;

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setTempPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(masterConfigRegister) |= LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

            /* Discard same samples if interrupts are generated during INT enable before switch on sensor */
            T(sensors[TEMP]).samplesToDiscard = 255;

            SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
        } else {
            T(accelSensorDependencies) &= ~BIT(TEMP);
            T(embeddedFunctionsDependencies) &= ~BIT(TEMP);

            if (T(sensors[PRESS]).enabled) {
                i = lsm6dsm_computeOdr(T(sensors[PRESS]).rate);
                reg_value |= LSM6DSM_SENSOR_SLAVE_BARO_RATES_REG_VALUE(i);
            } else
                reg_value |= LSM6DSM_SENSOR_SLAVE_BARO_POWER_OFF_VALUE;

            SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_BARO_POWER_ADDR, reg_value,
                    T(sensors[TEMP]).hwRate, PRESS);

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
            if (!(T(sensors[MAGN].enabled) || T(sensors[PRESS].enabled))) {
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_MASTER_ON;
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

                SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
            }
#else /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
            if (!T(sensors[PRESS].enabled)) {
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_MASTER_ON;
                T(masterConfigRegister) &= ~LSM6DSM_MASTER_CONFIG_DRDY_ON_INT1;

                SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, T(masterConfigRegister));
            }
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

            if (T(embeddedFunctionsDependencies) == 0) {
                DEBUG_PRINT("setTempPower: no embedded sensors on, disabling digital functions\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_DIGITAL_FUNC;
                SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
            }

            T(sensors[TEMP]).hwRate = 0;

            lsm6dsm_updateAccelOdr();
        }

        /* If enable, set only INT bit enable (sensor will be switched on by setRate function).
           If disable, disable INT bit, disable sensor-hub and disable ODR (power-off sensor) */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[TEMP]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[TEMP]) = true;
        T(sensors[TEMP]).pConfig.enable = on;
    }

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

/*
 * lsm6dsm_setStepDetectorPower: enable/disable step detector sensor
 */
static bool lsm6dsm_setStepDetectorPower(bool on, void *cookie)
{
    TDECL();

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setStepDetectorPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(accelSensorDependencies) |= BIT(STEP_DETECTOR);
            T(embeddedFunctionsDependencies) |= BIT(STEP_DETECTOR);
            T(embeddedFunctionsRegister) |= (LSM6DSM_ENABLE_PEDOMETER_DIGITAL_FUNC | LSM6DSM_ENABLE_DIGITAL_FUNC);
            T(int1Register) |= LSM6DSM_INT_STEP_DETECTOR_ENABLE_REG_VALUE;

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
        } else {
            T(accelSensorDependencies) &= ~BIT(STEP_DETECTOR);
            T(embeddedFunctionsDependencies) &= ~BIT(STEP_DETECTOR);
            T(int1Register) &= ~LSM6DSM_INT_STEP_DETECTOR_ENABLE_REG_VALUE;

            if ((T(embeddedFunctionsDependencies) & (BIT(STEP_COUNTER) | BIT(SIGN_MOTION))) == 0) {
                DEBUG_PRINT("setStepDetectorPower: no more need pedometer algo, disabling it\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_PEDOMETER_DIGITAL_FUNC;
            }

            if (T(embeddedFunctionsDependencies) == 0) {
                DEBUG_PRINT("setStepDetectorPower: no embedded sensors on, disabling digital functions\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_DIGITAL_FUNC;
            }

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
            SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
        }

        /* If enable, set INT bit enable and enable accelerometer sensor @26Hz if disabled. If disable, disable INT bit and disable accelerometer if no one need it */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[STEP_DETECTOR]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[STEP_DETECTOR]) = true;
        T(sensors[STEP_DETECTOR]).pConfig.enable = on;
    }

    return true;
}

/*
 * lsm6dsm_setStepCounterPower: enable/disable step counter sensor
 */
static bool lsm6dsm_setStepCounterPower(bool on, void *cookie)
{
    TDECL();

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setStepCounterPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(readSteps) = false;
            T(accelSensorDependencies) |= BIT(STEP_COUNTER);
            T(embeddedFunctionsDependencies) |= BIT(STEP_COUNTER);
            T(embeddedFunctionsRegister) |= (LSM6DSM_ENABLE_PEDOMETER_DIGITAL_FUNC | LSM6DSM_ENABLE_DIGITAL_FUNC);
            T(int2Register) |= LSM6DSM_INT_STEP_COUNTER_ENABLE_REG_VALUE;

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
            SPI_WRITE(LSM6DSM_INT2_CTRL_ADDR, T(int2Register));
        } else {
            T(accelSensorDependencies) &= ~BIT(STEP_COUNTER);
            T(embeddedFunctionsDependencies) &= ~BIT(STEP_COUNTER);
            T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_TIMER_DIGITAL_FUNC;
            T(int2Register) &= ~LSM6DSM_INT_STEP_COUNTER_ENABLE_REG_VALUE;

            if ((T(embeddedFunctionsDependencies) & (BIT(STEP_DETECTOR) | BIT(SIGN_MOTION))) == 0) {
                DEBUG_PRINT("setStepCounterPower: no more need pedometer algo, disabling it\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_PEDOMETER_DIGITAL_FUNC;
            }

            if (T(embeddedFunctionsDependencies) == 0) {
                DEBUG_PRINT("setStepCounterPower: no embedded sensors on, disabling digital functions\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_DIGITAL_FUNC;
            }

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_INT2_CTRL_ADDR, T(int2Register));
            SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
        }

        /* If enable, set INT bit enable and enable accelerometer sensor @26Hz if disabled. If disable, disable INT bit and disable accelerometer if no one need it */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[STEP_COUNTER]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[STEP_COUNTER]) = true;
        T(sensors[STEP_COUNTER]).pConfig.enable = on;
    }

    return true;
}

/*
 * lsm6dsm_setSignMotionPower: enable/disable significant motion sensor
 */
static bool lsm6dsm_setSignMotionPower(bool on, void *cookie)
{
    TDECL();

    /* If current status is SENSOR_IDLE set state to SENSOR_POWERING_* and execute command directly.
        If current status is NOT SENSOR_IDLE add pending config that will be managed before go back to SENSOR_IDLE. */
    if (trySwitchState(on ? SENSOR_POWERING_UP : SENSOR_POWERING_DOWN)) {
        INFO_PRINT("setSignMotionPower: %s\n", on ? "enable" : "disable");

        if (on) {
            T(accelSensorDependencies) |= BIT(SIGN_MOTION);
            T(embeddedFunctionsDependencies) |= BIT(SIGN_MOTION);
            T(embeddedFunctionsRegister) |= (LSM6DSM_ENABLE_SIGN_MOTION_DIGITAL_FUNC | LSM6DSM_ENABLE_PEDOMETER_DIGITAL_FUNC | LSM6DSM_ENABLE_DIGITAL_FUNC);
            T(int1Register) |= LSM6DSM_INT_SIGN_MOTION_ENABLE_REG_VALUE;

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
        } else {
            T(accelSensorDependencies) &= ~BIT(SIGN_MOTION);
            T(embeddedFunctionsDependencies) &= ~BIT(SIGN_MOTION);
            T(int1Register) &= ~LSM6DSM_INT_SIGN_MOTION_ENABLE_REG_VALUE;

            if ((T(embeddedFunctionsDependencies) & (BIT(STEP_DETECTOR) | BIT(STEP_COUNTER))) == 0) {
                DEBUG_PRINT("setSignMotionPower: no more need pedometer algo, disabling it\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_SIGN_MOTION_DIGITAL_FUNC;
            }

            if (T(embeddedFunctionsDependencies) == 0) {
                DEBUG_PRINT("setSignMotionPower: no embedded sensors on, disabling digital functions\n");
                T(embeddedFunctionsRegister) &= ~LSM6DSM_ENABLE_DIGITAL_FUNC;
            }

            lsm6dsm_updateAccelOdr();

            SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register), 50000);
            SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));
        }

        /* If enable, set INT bit enable and enable accelerometer sensor @26Hz if disabled. If disable, disable INT bit and disable accelerometer if no one need it */
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[SIGN_MOTION]), __FUNCTION__);
    } else {
        T(pendingEnableConfig[SIGN_MOTION]) = true;
        T(sensors[SIGN_MOTION]).pConfig.enable = on;
    }

    return true;
}

/*
 * lsm6dsm_accelFirmwareUpload: upload accelerometer firmware
 */
static bool lsm6dsm_accelFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[ACCEL]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}

/*
 * lsm6dsm_gyroFirmwareUpload: upload gyroscope firmware
 */
static bool lsm6dsm_gyroFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[GYRO]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
/*
 * lsm6dsm_magnFirmwareUpload: upload magnetometer firmware
 */
static bool lsm6dsm_magnFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[MAGN]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
/*
 * lsm6dsm_pressFirmwareUpload: upload pressure firmware
 */
static bool lsm6dsm_pressFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[PRESS]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}

/*
 * lsm6dsm_tempFirmwareUpload: upload pressure firmware
 */
static bool lsm6dsm_tempFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[TEMP]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

/*
 * lsm6dsm_stepDetectorFirmwareUpload: upload step detector firmware
 */
static bool lsm6dsm_stepDetectorFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[STEP_DETECTOR]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}

/*
 * lsm6dsm_stepCounterFirmwareUpload: upload step counter firmware
 */
static bool lsm6dsm_stepCounterFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[STEP_COUNTER]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}

/*
 * lsm6dsm_signMotionFirmwareUpload: upload significant motion firmware
 */
static bool lsm6dsm_signMotionFirmwareUpload(void *cookie)
{
    TDECL();

    sensorSignalInternalEvt(T(sensors[SIGN_MOTION]).handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);

    return true;
}

/*
 * lsm6dsm_setAccelRate: set accelerometer ODR and report latency (FIFO watermark related)
 */
static bool lsm6dsm_setAccelRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();

    if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
        INFO_PRINT("setAccelRate: rate=%dHz, latency=%lldns\n", (int)(rate / 1024), latency);

        T(sensors[ACCEL]).rate = rate;
        T(sensors[ACCEL]).latency = latency;

        lsm6dsm_updateAccelOdr();

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[ACCEL]), __FUNCTION__);
    } else {
        T(pendingRateConfig[ACCEL]) = true;
        T(sensors[ACCEL].pConfig.rate) = rate;
        T(sensors[ACCEL]).pConfig.latency = latency;
    }

    return true;
}

/*
 * lsm6dsm_setGyroRate: set gyroscope ODR and report latency (FIFO watermark related)
 */
static bool lsm6dsm_setGyroRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();
    uint8_t i;

    if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
        INFO_PRINT("setGyroRate: rate=%dHz, latency=%lldns\n", (int)(rate / 1024), latency);

        /* This call return index of LSM6DSMImuRates struct element */
        i = lsm6dsm_computeOdr(rate);

        T(sensors[GYRO]).rate = rate;
        T(sensors[GYRO]).latency = latency;
        T(sensors[GYRO]).samplesToDiscard = LSM6DSMGyroRatesSamplesToDiscard[i];

        if (T(sensors[GYRO]).hwRate == 0)
            T(sensors[GYRO]).samplesToDiscard += LSM6DSMRatesSamplesToDiscardGyroPowerOn[i];

        T(sensors[GYRO]).hwRate = rate < (SENSOR_HZ(26.0f) / 2) ? (SENSOR_HZ(26.0f) / 2) : rate;
        T(sensors[GYRO]).samplesDecimator = T(sensors[GYRO]).hwRate / rate;
        T(sensors[GYRO]).samplesCounter = T(sensors[GYRO]).samplesDecimator - 1;

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
        /* Gyroscope bias calibration library requires accelerometer data, enabling it @26Hz if not enabled */
        T(accelSensorDependencies) |= BIT(GYRO);
        T(int1Register) |= LSM6DSM_INT_ACCEL_ENABLE_REG_VALUE;

        lsm6dsm_updateAccelOdr();

        SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, T(int1Register));
#else /* LSM6DSM_GYRO_CALIB_ENABLED */
        lsm6dsm_setTriggerRate();
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

        SPI_WRITE(LSM6DSM_CTRL2_G_ADDR, LSM6DSM_CTRL2_G_BASE | LSM6DSMImuRatesRegValue[i]);

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[GYRO]), __FUNCTION__);
    } else {
        T(pendingRateConfig[GYRO]) = true;
        T(sensors[GYRO]).pConfig.rate = rate;
        T(sensors[GYRO]).pConfig.latency = latency;
    }

    return true;
}

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
/*
 * lsm6dsm_setMagnRate: set magnetometer ODR and report latency (FIFO watermark related)
 */
static bool lsm6dsm_setMagnRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();
    uint8_t i, buffer[2];

    if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
        INFO_PRINT("setMagnRate: rate=%dHz, latency=%lldns\n", (int)(rate / 1024), latency);

        T(embeddedFunctionsDependencies) |= BIT(MAGN);
        T(embeddedFunctionsRegister) |= LSM6DSM_ENABLE_DIGITAL_FUNC;
        T(accelSensorDependencies) |= BIT(MAGN);

        T(sensors[MAGN]).rate = rate;
        T(sensors[MAGN]).latency = latency;

        lsm6dsm_updateAccelOdr();

        T(masterConfigRegister) |= LSM6DSM_MASTER_CONFIG_MASTER_ON;

        buffer[0] = T(embeddedFunctionsRegister); /* LSM6DSM_CTRL10_C */
        buffer[1] = T(masterConfigRegister);      /* LSM6DSM_MASTER_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_CTRL10_C_ADDR, buffer, 2);

        /* This call return index of LSM6DSMImuRates struct element */
        i = lsm6dsm_computeOdr(rate);
        T(sensors[MAGN]).hwRate = LSM6DSMSHRates[i];

#ifdef LSM6DSM_I2C_MASTER_LSM303AGR
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_MAGN_ODR_ADDR,
                LSM6DSM_SENSOR_SLAVE_MAGN_ODR_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ON_VALUE | LSM6DSM_SENSOR_SLAVE_MAGN_RATES_REG_VALUE(i),
                T(sensors[ACCEL]).hwRate, MAGN);
#else /* LSM6DSM_I2C_MASTER_LSM303AGR */
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ADDR,
                LSM6DSM_SENSOR_SLAVE_MAGN_POWER_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_POWER_ON_VALUE,
                T(sensors[ACCEL]).hwRate, MAGN);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_MAGN_ODR_ADDR,
                LSM6DSM_SENSOR_SLAVE_MAGN_ODR_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_RATES_REG_VALUE(i),
                T(sensors[ACCEL]).hwRate, MAGN);
#endif /* LSM6DSM_I2C_MASTER_LSM303AGR */

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[MAGN]), __FUNCTION__);
    } else {
        T(pendingRateConfig[MAGN]) = true;
        T(sensors[MAGN]).pConfig.rate = rate;
        T(sensors[MAGN]).pConfig.latency = latency;
    }

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
/*
 * lsm6dsm_setPressRate: set pressure ODR and report latency (FIFO watermark related)
 */
static bool lsm6dsm_setPressRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();
    uint8_t i, buffer[2];

    if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
        INFO_PRINT("setPressRate: rate=%dHz, latency=%lldns\n", (int)(rate / 1024), latency);

        T(embeddedFunctionsDependencies) |= BIT(PRESS);
        T(embeddedFunctionsRegister) |= LSM6DSM_ENABLE_DIGITAL_FUNC;
        T(accelSensorDependencies) |= BIT(PRESS);

        T(sensors[PRESS]).rate = rate;
        T(sensors[PRESS]).latency = latency;

        lsm6dsm_updateAccelOdr();

        T(masterConfigRegister) |= LSM6DSM_MASTER_CONFIG_MASTER_ON;

        buffer[0] = T(embeddedFunctionsRegister); /* LSM6DSM_CTRL10_C */
        buffer[1] = T(masterConfigRegister);      /* LSM6DSM_MASTER_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_CTRL10_C_ADDR, buffer, 2);

        if (T(sensors[TEMP]).enabled) {
            if (rate < T(sensors[TEMP]).rate)
                rate = T(sensors[TEMP]).rate;
        }

        /* This call return index of LSM6DSMImuRates struct element */
        i = lsm6dsm_computeOdr(rate);
        T(sensors[PRESS]).hwRate = LSM6DSMSHRates[i];

        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_BARO_ODR_ADDR,
                LSM6DSM_SENSOR_SLAVE_BARO_ODR_BASE | LSM6DSM_SENSOR_SLAVE_BARO_RATES_REG_VALUE(i),
                T(sensors[ACCEL]).hwRate, PRESS);

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[PRESS]), __FUNCTION__);
    } else {
        T(pendingRateConfig[PRESS]) = true;
        T(sensors[PRESS]).pConfig.rate = rate;
        T(sensors[PRESS]).pConfig.latency = latency;
    }

    return true;
}

/*
 * lsm6dsm_setTempRate: set temperature ODR and report latency (FIFO watermark related)
 */
static bool lsm6dsm_setTempRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();
    uint8_t i, buffer[2];

    if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
        INFO_PRINT("setTempRate: rate=%dHz, latency=%lldns\n", (int)(rate / 1024), latency);

        T(embeddedFunctionsDependencies) |= BIT(TEMP);
        T(embeddedFunctionsRegister) |= LSM6DSM_ENABLE_DIGITAL_FUNC;
        T(accelSensorDependencies) |= BIT(TEMP);

        T(sensors[TEMP]).rate = rate;
        T(sensors[TEMP]).hwRate = rate;
        T(sensors[TEMP]).latency = latency;

        lsm6dsm_updateAccelOdr();

        T(masterConfigRegister) |= LSM6DSM_MASTER_CONFIG_MASTER_ON;

        buffer[0] = T(embeddedFunctionsRegister); /* LSM6DSM_CTRL10_C */
        buffer[1] = T(masterConfigRegister);      /* LSM6DSM_MASTER_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_CTRL10_C_ADDR, buffer, 2);

        if (T(sensors[PRESS]).enabled) {
            if (rate < T(sensors[PRESS]).rate)
                rate = T(sensors[PRESS]).rate;
        }

        /* This call return index of LSM6DSMImuRates struct element */
        i = lsm6dsm_computeOdr(rate);
        T(sensors[TEMP]).hwRate = LSM6DSMSHRates[i];

        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_BARO_ODR_ADDR,
                LSM6DSM_SENSOR_SLAVE_BARO_ODR_BASE | LSM6DSM_SENSOR_SLAVE_BARO_RATES_REG_VALUE(i),
                T(sensors[ACCEL]).hwRate, TEMP);

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[TEMP]), __FUNCTION__);
    } else {
        T(pendingRateConfig[TEMP]) = true;
        T(sensors[TEMP]).pConfig.rate = rate;
        T(sensors[TEMP]).pConfig.latency = latency;
    }

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

/*
 * lsm6dsm_setStepDetectorRate: set step detector report latency
 */
static bool lsm6dsm_setStepDetectorRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();

    INFO_PRINT("setStepDetectorRate: latency=%lldns\n", latency);

    T(sensors[STEP_DETECTOR]).rate = rate;
    T(sensors[STEP_DETECTOR]).latency = latency;

    sensorSignalInternalEvt(T(sensors[STEP_DETECTOR]).handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);

    return true;
}

/*
 * lsm6dsm_setStepCounterRate: set step counter report latency
 */
static bool lsm6dsm_setStepCounterRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();
    uint8_t i, step_delta_reg;

    if (rate == SENSOR_RATE_ONCHANGE) {
        INFO_PRINT("setStepCounterRate: delivery-rate=on_change, latency=%lldns\n", latency);
    } else
        INFO_PRINT("setStepCounterRate: delivery_rate=%dms, latency=%lldns\n", (int)((1024.0f / rate) * 1000.0f), latency);

    if (trySwitchState(SENSOR_CONFIG_CHANGING)) {
        T(sensors[STEP_COUNTER]).rate = rate;
        T(sensors[STEP_COUNTER]).latency = latency;

        for (i = 0; i < ARRAY_SIZE(LSM6DSMStepCounterRates); i++) {
            if (rate == LSM6DSMStepCounterRates[i])
                break;
        }
        if (i >= (ARRAY_SIZE(LSM6DSMStepCounterRates) - 2))
            step_delta_reg = 0;
        else
            step_delta_reg = (128 >> i);

        T(embeddedFunctionsRegister) |= LSM6DSM_ENABLE_TIMER_DIGITAL_FUNC;
        SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, T(embeddedFunctionsRegister));

        lsm6dsm_writeEmbeddedRegister(LSM6DSM_EMBEDDED_STEP_COUNT_DELTA_ADDR, step_delta_reg);

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[GYRO]), __FUNCTION__);
    } else {
        T(pendingRateConfig[STEP_COUNTER]) = true;
        T(sensors[STEP_COUNTER]).pConfig.rate = rate;
        T(sensors[STEP_COUNTER]).pConfig.latency = latency;
    }

    return true;
}

/*
 * lsm6dsm_setSignMotionRate: set significant motion report latency
 */
static bool lsm6dsm_setSignMotionRate(uint32_t rate, uint64_t latency, void *cookie)
{
    TDECL();

    DEBUG_PRINT("setSignMotionRate: rate=%dHz, latency=%lldns\n", (int)(rate / 1024), latency);

    T(sensors[SIGN_MOTION]).rate = rate;
    T(sensors[SIGN_MOTION]).latency = latency;

    sensorSignalInternalEvt(T(sensors[SIGN_MOTION]).handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);

    return true;
}

/*
 * lsm6dsm_accelFlush: send accelerometer flush event
 */
static bool lsm6dsm_accelFlush(void *cookie)
{
    INFO_PRINT("accelFlush\n");

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ACCEL), SENSOR_DATA_EVENT_FLUSH, NULL);

    return true;
}

/*
 * lsm6dsm_gyroFlush: send gyroscope flush event
 */
static bool lsm6dsm_gyroFlush(void *cookie)
{
    INFO_PRINT("gyroFlush\n");

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_GYRO), SENSOR_DATA_EVENT_FLUSH, NULL);

    return true;
}

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
/*
 * lsm6dsm_magnFlush: send magnetometer flush event
 */
static bool lsm6dsm_magnFlush(void *cookie)
{
    INFO_PRINT("magnFlush\n");

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_MAG), SENSOR_DATA_EVENT_FLUSH, NULL);

    return true;
}
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
/*
 * lsm6dsm_pressFlush: send pressure flush event
 */
static bool lsm6dsm_pressFlush(void *cookie)
{
    return true;
}

/*
 * lsm6dsm_tempFlush: send temperature flush event
 */
static bool lsm6dsm_tempFlush(void *cookie)
{
    return true;
}
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

/*
 * lsm6dsm_stepDetectorFlush: send step detector flush event
 */
static bool lsm6dsm_stepDetectorFlush(void *cookie)
{
    INFO_PRINT("stepDetectorFlush\n");

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_STEP_DETECT), SENSOR_DATA_EVENT_FLUSH, NULL);

    return true;
}

/*
 * lsm6dsm_stepCounterFlush: send step counter flush event
 */
static bool lsm6dsm_stepCounterFlush(void *cookie)
{
    INFO_PRINT("stepCounterFlush\n");

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_STEP_COUNT), SENSOR_DATA_EVENT_FLUSH, NULL);

    return true;
}

/*
 * lsm6dsm_signMotionFlush: send significant motion flush event
 */
static bool lsm6dsm_signMotionFlush(void *cookie)
{
    INFO_PRINT("signMotionFlush\n");

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_SIG_MOTION), SENSOR_DATA_EVENT_FLUSH, NULL);

    return true;
}

/*
 * lsm6dsm_stepCounterSendLastData: send last number of steps
 */
static bool lsm6dsm_stepCounterSendLastData(void *cookie, uint32_t tid)
{
    TDECL();

    INFO_PRINT("stepCounterSendLastData: %lu steps\n", T(totalNumSteps));

    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_STEP_COUNT), &T(totalNumSteps), NULL);

    return true;
}

/*
 * lsm6dsm_sensorInit: initial sensor configuration
 */
static void lsm6dsm_sensorInit(void)
{
    TDECL();
    uint8_t buffer[4];

    switch (T(initState)) {
    case RESET_LSM6DSM:
        INFO_PRINT("Performing soft-reset\n");

        T(initState) = INIT_LSM6DSM;

        /* Sensor SW-reset */
        SPI_WRITE(LSM6DSM_CTRL3_C_ADDR, LSM6DSM_SW_RESET, 20000);

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case INIT_LSM6DSM:
        INFO_PRINT("Initial registers configuration\n");

        /* During init, reset all configurable registers to default values */
        SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE, 50);
        SPI_WRITE(LSM6DSM_DRDY_PULSE_CFG_ADDR, LSM6DSM_DRDY_PULSE_CFG_BASE);

        buffer[0] = LSM6DSM_CTRL1_XL_BASE;                           /* LSM6DSM_CTRL1_XL */
        buffer[1] = LSM6DSM_CTRL2_G_BASE;                            /* LSM6DSM_CTRL2_G */
        buffer[2] = LSM6DSM_CTRL3_C_BASE;                            /* LSM6DSM_CTRL3_C */
        buffer[3] = LSM6DSM_CTRL4_C_BASE;                            /* LSM6DSM_CTRL4_C */
        SPI_MULTIWRITE(LSM6DSM_CTRL1_XL_ADDR, buffer, 4);

        buffer[0] = LSM6DSM_CTRL10_C_BASE | LSM6DSM_RESET_PEDOMETER; /* LSM6DSM_CTRL10_C */
        buffer[1] = LSM6DSM_MASTER_CONFIG_BASE;                      /* LSM6DSM_MASTER_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_CTRL10_C_ADDR, buffer, 2);

        SPI_WRITE(LSM6DSM_INT1_CTRL_ADDR, LSM6DSM_INT1_CTRL_BASE);

#ifdef LSM6DSM_I2C_MASTER_ENABLED
        T(initState) = INIT_I2C_MASTER_REGS_CONF;
#else /* LSM6DSM_I2C_MASTER_ENABLED */
        INFO_PRINT("Initialization completed successfully!\n");
        T(initState) = INIT_DONE;
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

#ifdef LSM6DSM_I2C_MASTER_ENABLED
    case INIT_I2C_MASTER_REGS_CONF:
        INFO_PRINT("Initial I2C master registers configuration\n");

        /* Enable access for embedded registers */
        SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE | LSM6DSM_ENABLE_FUNC_CFG_ACCESS, 50);

        /* I2C-0 configuration */
        buffer[0] = LSM6DSM_EMBEDDED_SLV0_WRITE_ADDR_SLEEP;                                               /* LSM6DSM_EMBEDDED_SLV0_ADDR */
        buffer[1] = 0x00;                                                                                 /* LSM6DSM_EMBEDDED_SLV0_SUBADDR */
        buffer[2] = LSM6DSM_EMBEDDED_SENSOR_HUB_NUM_SLAVE;                                                /* LSM6DSM_EMBEDDED_SLV0_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV0_ADDR_ADDR, buffer, 3);

#if defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)     /* Magn & Baro both enabled */
        /* I2C-1 configuration */
        buffer[0] = (LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT << 1) | LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB; /* LSM6DSM_EMBEDDED_SLV1_ADDR */
        buffer[1] = LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_ADDR;                                               /* LSM6DSM_EMBEDDED_SLV1_SUBADDR */
        buffer[2] = LSM6DSM_EMBEDDED_SLV1_CONFIG_WRITE_ONCE | LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN;      /* LSM6DSM_EMBEDDED_SLV1_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV1_ADDR_ADDR, buffer, 3);

        /* I2C-2 configuration */
        buffer[0] = (LSM6DSM_SENSOR_SLAVE_BARO_I2C_ADDR_8BIT << 1) | LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB; /* LSM6DSM_EMBEDDED_SLV2_ADDR */
        buffer[1] = LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_ADDR;                                               /* LSM6DSM_EMBEDDED_SLV2_SUBADDR */
        buffer[2] = LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN;                                                /* LSM6DSM_EMBEDDED_SLV2_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV2_ADDR_ADDR, buffer, 3);

#ifdef LSM6DSM_I2C_MASTER_AK09916
        /* I2C-3 configuration */
        buffer[0] = (LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT << 1) | LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB; /* LSM6DSM_EMBEDDED_SLV3_ADDR */
        buffer[1] = AK09916_STATUS_DATA_ADDR;                                                             /* LSM6DSM_EMBEDDED_SLV3_SUBADDR */
        buffer[2] = 1;                                                                                    /* LSM6DSM_EMBEDDED_SLV3_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV3_ADDR_ADDR, buffer, 3);
#endif /* LSM6DSM_I2C_MASTER_AK09916 */
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED) */

#if defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && !defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)    /* Magn only enabled */
        /* I2C-1 configuration */
        buffer[0] = (LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT << 1) | LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB; /* LSM6DSM_EMBEDDED_SLV1_ADDR */
        buffer[1] = LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_ADDR;                                               /* LSM6DSM_EMBEDDED_SLV1_SUBADDR */
        buffer[2] = LSM6DSM_EMBEDDED_SLV1_CONFIG_WRITE_ONCE | LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN;      /* LSM6DSM_EMBEDDED_SLV1_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV1_ADDR_ADDR, buffer, 3);

#ifdef LSM6DSM_I2C_MASTER_AK09916
        /* I2C-2 configuration */
        buffer[0] = (LSM6DSM_SENSOR_SLAVE_MAGN_I2C_ADDR_8BIT << 1) | LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB; /* LSM6DSM_EMBEDDED_SLV2_ADDR */
        buffer[1] = AK09916_STATUS_DATA_ADDR;                                                             /* LSM6DSM_EMBEDDED_SLV2_SUBADDR */
        buffer[2] = 1;                                                                                    /* LSM6DSM_EMBEDDED_SLV2_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV2_ADDR_ADDR, buffer, 3);
#endif /* LSM6DSM_I2C_MASTER_AK09916 */
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED) */

#if !defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)    /* Baro only enabled */
        /* I2C-1 configuration */
        buffer[0] = (LSM6DSM_SENSOR_SLAVE_BARO_I2C_ADDR_8BIT << 1) | LSM6DSM_EMBEDDED_READ_OP_SENSOR_HUB; /* LSM6DSM_EMBEDDED_SLV1_ADDR */
        buffer[1] = LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_ADDR;                                               /* LSM6DSM_EMBEDDED_SLV1_SUBADDR */
        buffer[2] = LSM6DSM_EMBEDDED_SLV1_CONFIG_WRITE_ONCE | LSM6DSM_SENSOR_SLAVE_BARO_OUTDATA_LEN;      /* LSM6DSM_EMBEDDED_SLV1_CONFIG */
        SPI_MULTIWRITE(LSM6DSM_EMBEDDED_SLV1_ADDR_ADDR, buffer, 3);
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED) */

        /* Disable access for embedded registers */
        SPI_WRITE(LSM6DSM_FUNC_CFG_ACCESS_ADDR, LSM6DSM_FUNC_CFG_ACCESS_BASE, 50);

        T(initState) = INIT_I2C_MASTER_SENSOR_RESET;

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case INIT_I2C_MASTER_SENSOR_RESET:
        INFO_PRINT("Performing soft-reset slave sensors\n");
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
        T(initState) = INIT_I2C_MASTER_MAGN_SENSOR;
#else /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
        T(initState) = INIT_I2C_MASTER_BARO_SENSOR;
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

        /* Enable accelerometer and sensor-hub to initialize slave sensor */
        SPI_WRITE(LSM6DSM_CTRL1_XL_ADDR, LSM6DSM_CTRL1_XL_BASE | LSM6DSM_ODR_104HZ_REG_VALUE);
        SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, LSM6DSM_CTRL10_C_BASE | LSM6DSM_ENABLE_DIGITAL_FUNC);
        SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, LSM6DSM_MASTER_CONFIG_BASE | LSM6DSM_MASTER_CONFIG_MASTER_ON);

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_MAGN_RESET_ADDR, LSM6DSM_SENSOR_SLAVE_MAGN_RESET_VALUE, SENSOR_HZ(104.0f), MAGN, 20000);
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM6DSM_SENSOR_SLAVE_BARO_RESET_ADDR, LSM6DSM_SENSOR_SLAVE_BARO_RESET_VALUE, SENSOR_HZ(104.0f), PRESS, 20000);
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case INIT_I2C_MASTER_MAGN_SENSOR:
        INFO_PRINT("Initial slave magnetometer sensor registers configuration\n");
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        T(initState) = INIT_I2C_MASTER_BARO_SENSOR;
#else /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
        T(initState) = INIT_I2C_MASTER_SENSOR_END;
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_LIS3MDL
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LIS3MDL_CTRL1_ADDR, LIS3MDL_CTRL1_BASE, SENSOR_HZ(104.0f), MAGN);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LIS3MDL_CTRL2_ADDR, LIS3MDL_CTRL2_BASE, SENSOR_HZ(104.0f), MAGN);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LIS3MDL_CTRL3_ADDR, LIS3MDL_CTRL3_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE, SENSOR_HZ(104.0f), MAGN);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LIS3MDL_CTRL4_ADDR, LIS3MDL_CTRL4_BASE, SENSOR_HZ(104.0f), MAGN);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LIS3MDL_CTRL5_ADDR, LIS3MDL_CTRL5_BASE, SENSOR_HZ(104.0f), MAGN);
#endif /* LSM6DSM_I2C_MASTER_LIS3MDL */

#ifdef LSM6DSM_I2C_MASTER_LSM303AGR
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM303AGR_CFG_REG_A_M_ADDR, LSM303AGR_CFG_REG_A_M_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE, SENSOR_HZ(104.0f), MAGN);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LSM303AGR_CFG_REG_C_M_ADDR, LSM303AGR_CFG_REG_C_M_BASE, SENSOR_HZ(104.0f), MAGN);
#endif /* LSM6DSM_I2C_MASTER_LSM303AGR */

#ifdef LSM6DSM_I2C_MASTER_AK09916
        SPI_WRITE_SLAVE_SENSOR_REGISTER(AK09916_CNTL2_ADDR, AK09916_CNTL2_BASE | LSM6DSM_SENSOR_SLAVE_MAGN_POWER_OFF_VALUE, SENSOR_HZ(104.0f), MAGN);
#endif /* LSM6DSM_I2C_MASTER_AK09916 */

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case INIT_I2C_MASTER_BARO_SENSOR:
        INFO_PRINT("Initial slave barometer sensor registers configuration\n");
        T(initState) = INIT_I2C_MASTER_SENSOR_END;

#ifdef LSM6DSM_I2C_MASTER_LPS22HB
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LPS22HB_CTRL1_ADDR, LPS22HB_CTRL1_BASE | LSM6DSM_SENSOR_SLAVE_BARO_POWER_OFF_VALUE, SENSOR_HZ(104.0f), PRESS);
        SPI_WRITE_SLAVE_SENSOR_REGISTER(LPS22HB_CTRL2_ADDR, LPS22HB_CTRL2_BASE, SENSOR_HZ(104.0f), PRESS);
#endif /* LSM6DSM_I2C_MASTER_LPS22HB */

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case INIT_I2C_MASTER_SENSOR_END:
        INFO_PRINT("Initialization completed successfully!\n");
        T(initState) = INIT_DONE;

        /* Disable accelerometer and sensor-hub */
        SPI_WRITE(LSM6DSM_MASTER_CONFIG_ADDR, LSM6DSM_MASTER_CONFIG_BASE);
        SPI_WRITE(LSM6DSM_CTRL10_C_ADDR, LSM6DSM_CTRL10_C_BASE);
        SPI_WRITE(LSM6DSM_CTRL1_XL_ADDR, LSM6DSM_CTRL1_XL_BASE);

        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

    default:
        break;
    }
}

/*
 * lsm6dsm_processPendingEvt: process pending events
 */
static void lsm6dsm_processPendingEvt(void)
{
    TDECL();
    enum SensorIndex i;

    if (T(pendingInt[LSM6DSM_INT1_INDEX])) {
        T(pendingInt[LSM6DSM_INT1_INDEX]) = false;
        lsm6dsm_readStatusReg(false);
        return;
    }

    for (i = ACCEL; i < NUM_SENSORS; i++) {
        if (T(pendingEnableConfig[i])) {
            T(pendingEnableConfig[i]) = false;
            LSM6DSMSensorOps[i].sensorPower(T(sensors[i]).pConfig.enable, (void *)i);
            return;
        }

        if (T(pendingRateConfig[i])) {
            T(pendingRateConfig[i]) = false;
            LSM6DSMSensorOps[i].sensorSetRate(T(sensors[i]).pConfig.rate, T(sensors[i]).pConfig.latency, (void *)i);
            return;
        }
    }
}

/*
 * lsm6dsm_processSensorThreeAxisData: elaborate three axis sensors data
 */
static bool lsm6dsm_processSensorThreeAxisData(struct LSM6DSMSensor *mSensor, uint8_t *data)
{
    TDECL();
    float kScale = 1.0f, x, y, z;

    switch (mSensor->idx) {
    case ACCEL:
        kScale = LSM6DSM_ACCEL_KSCALE;
        break;
    case GYRO:
        kScale = LSM6DSM_GYRO_KSCALE;
        break;
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    case MAGN:
        kScale = LSM6DSM_MAGN_KSCALE;
        break;
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
    default:
        return false;
    }

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

    mSensor->tADataEvt->referenceTime = T(timestampInt[LSM6DSM_INT1_INDEX]);
    mSensor->tADataEvt->samples[0].firstSample.numSamples = 1;

    switch (mSensor->idx) {
    case ACCEL:
    case GYRO:
        mSensor->tADataEvt->samples[0].x = LSM6DSM_REMAP_X_DATA(x, y, z, LSM6DSM_ROT_MATRIX);
        mSensor->tADataEvt->samples[0].y = LSM6DSM_REMAP_Y_DATA(x, y, z, LSM6DSM_ROT_MATRIX);
        mSensor->tADataEvt->samples[0].z = LSM6DSM_REMAP_Z_DATA(x, y, z, LSM6DSM_ROT_MATRIX);
        break;

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    case MAGN:
        mSensor->tADataEvt->samples[0].x = LSM6DSM_REMAP_X_DATA(x, y, z, LSM6DSM_MAGN_ROT_MATRIX);
        mSensor->tADataEvt->samples[0].y = LSM6DSM_REMAP_Y_DATA(x, y, z, LSM6DSM_MAGN_ROT_MATRIX);
        mSensor->tADataEvt->samples[0].z = LSM6DSM_REMAP_Z_DATA(x, y, z, LSM6DSM_MAGN_ROT_MATRIX);
        break;
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */

    default:
        break;
    }

    if (mSensor->samplesToDiscard == 0) {
        mSensor->samplesCounter++;

        if (mSensor->samplesCounter >= mSensor->samplesDecimator) {
            switch (mSensor->idx) {
            case ACCEL:
#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
                accelCalRun(&T(accelCal), T(timestampInt[LSM6DSM_INT1_INDEX]),
                    mSensor->tADataEvt->samples[0].x,
                    mSensor->tADataEvt->samples[0].y,
                    mSensor->tADataEvt->samples[0].z, T(currentTemperature));

                accelCalBiasRemove(&T(accelCal),
                    &mSensor->tADataEvt->samples[0].x,
                    &mSensor->tADataEvt->samples[0].y,
                    &mSensor->tADataEvt->samples[0].z);
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
                if (T(sensors[GYRO].enabled)) {
                    gyroCalUpdateAccel(&T(gyroCal), T(timestampInt[LSM6DSM_INT1_INDEX]),
                        mSensor->tADataEvt->samples[0].x,
                        mSensor->tADataEvt->samples[0].y,
                        mSensor->tADataEvt->samples[0].z);
                }
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
                break;

            case GYRO:
#ifdef LSM6DSM_GYRO_CALIB_ENABLED
                gyroCalUpdateGyro(&T(gyroCal), T(timestampInt[LSM6DSM_INT1_INDEX]),
                    mSensor->tADataEvt->samples[0].x,
                    mSensor->tADataEvt->samples[0].y,
                    mSensor->tADataEvt->samples[0].z, T(currentTemperature));

                gyroCalRemoveBias(&T(gyroCal),
                    mSensor->tADataEvt->samples[0].x,
                    mSensor->tADataEvt->samples[0].y,
                    mSensor->tADataEvt->samples[0].z,
                    &mSensor->tADataEvt->samples[0].x,
                    &mSensor->tADataEvt->samples[0].y,
                    &mSensor->tADataEvt->samples[0].z);
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
                break;

#ifdef LSM6DSM_MAGN_CALIB_ENABLED
            case MAGN: ;
                float magnOffX, magnOffY, magnOffZ;

                magCalRemoveSoftiron(&T(magnCal),
                    mSensor->tADataEvt->samples[0].x,
                    mSensor->tADataEvt->samples[0].y,
                    mSensor->tADataEvt->samples[0].z,
                    &magnOffX, &magnOffY, &magnOffZ);

                T(newMagnCalibData) = magCalUpdate(&T(magnCal), NS_TO_US(T(timestampInt[LSM6DSM_INT1_INDEX])), magnOffX, magnOffY, magnOffZ);

                magCalRemoveBias(&T(magnCal), magnOffX, magnOffY, magnOffZ,
                    &mSensor->tADataEvt->samples[0].x,
                    &mSensor->tADataEvt->samples[0].y,
                    &mSensor->tADataEvt->samples[0].z);
                break;
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */

            default:
                break;
            }

            mSensor->samplesCounter = 0;
            return true;
        }
    } else
        mSensor->samplesToDiscard--;

    return false;
}

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
/*
 * lsm6dsm_processSensorOneAxisData: elaborate single axis sensors data
 */
static bool lsm6dsm_processSensorOneAxisData(struct LSM6DSMSensor *mSensor, uint8_t *data, void *store)
{
    TDECL();
    struct SingleAxisDataEvent *pressData = (struct SingleAxisDataEvent *)store;
    union EmbeddedDataPoint *tempData = (union EmbeddedDataPoint *)store;

    if (mSensor->samplesToDiscard == 0) {
        mSensor->samplesCounter++;

        if (mSensor->samplesCounter >= mSensor->samplesDecimator) {
            switch (mSensor->idx) {
            case PRESS:
                pressData->samples[0].fdata = ((data[2] << 16) | (data[1] << 8) | data[0]) * LSM6DSM_PRESS_KSCALE;
                pressData->referenceTime = T(timestampInt[LSM6DSM_INT1_INDEX]);
                pressData->samples[0].firstSample.numSamples = 1;
                break;
            case TEMP:
                tempData->fdata = ((int16_t)(data[1] << 8) | data[0]) * LSM6DSM_TEMP_KSCALE;
                break;
            default:
                return false;
            }

            mSensor->samplesCounter = 0;
            return true;
        }
    } else
            mSensor->samplesToDiscard--;

    return false;
}
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

/*
 * lsm6dsm_handleSpiDoneEvt: all SPI operation fall back here
 */
static void lsm6dsm_handleSpiDoneEvt(const void *evtData)
{
    TDECL();
    bool returnIdle = false, validData;
    struct LSM6DSMSensor *mSensor;
    int i;

    switch (GET_STATE()) {
    case SENSOR_BOOT:
        SET_STATE(SENSOR_VERIFY_WAI);

        SPI_READ(LSM6DSM_WAI_ADDR, 1, &T_SLAVE_INTERFACE(tmpDataBuffer));
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case SENSOR_VERIFY_WAI:
        if (T_SLAVE_INTERFACE(tmpDataBuffer[1]) != LSM6DSM_WAI_VALUE) {
            T(mRetryLeft)--;
            if (T(mRetryLeft) == 0)
                break;

            ERROR_PRINT("`Who-Am-I` register value not valid: %x\n", T_SLAVE_INTERFACE(tmpDataBuffer[1]));
            SET_STATE(SENSOR_BOOT);
            timTimerSet(100000000, 100, 100, lsm6dsm_timerCallback, NULL, true);
        } else {
            SET_STATE(SENSOR_INITIALIZATION);
            T(initState) = RESET_LSM6DSM;
            lsm6dsm_sensorInit();
        }

        break;

    case SENSOR_INITIALIZATION:
        if (T(initState) == INIT_DONE) {
            for (i = 0; i < NUM_SENSORS; i++)
                sensorRegisterInitComplete(T(sensors[i]).handle);

            returnIdle = true;
        } else
            lsm6dsm_sensorInit();

        break;

    case SENSOR_POWERING_UP:
        mSensor = (struct LSM6DSMSensor *)evtData;

        mSensor->enabled = true;
        sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, 1, 0);
        returnIdle = true;
        break;

    case SENSOR_POWERING_DOWN:
        mSensor = (struct LSM6DSMSensor *)evtData;

        mSensor->enabled = false;
        sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, 0, 0);
        returnIdle = true;
        break;

    case SENSOR_CONFIG_CHANGING:
        mSensor = (struct LSM6DSMSensor *)evtData;

        sensorSignalInternalEvt(mSensor->handle, SENSOR_INTERNAL_EVT_RATE_CHG, mSensor->rate, mSensor->latency);
        returnIdle = true;
        break;

    case SENSOR_INT1_STATUS_REG_HANDLING:
        if (T(sensors[STEP_DETECTOR].enabled)) {
            if (T_SLAVE_INTERFACE(funcSrcBuffer[1]) & LSM6DSM_FUNC_SRC_STEP_DETECTED) {
                osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_STEP_DETECT), NULL, NULL);
                DEBUG_PRINT("Step Detected!\n");
            }
        }

        if (T(sensors[STEP_COUNTER].enabled)) {
            if (T_SLAVE_INTERFACE(funcSrcBuffer[1]) & LSM6DSM_FUNC_SRC_STEP_COUNT_DELTA_IA) {
                T(readSteps) = true;
                SPI_READ(LSM6DSM_STEP_COUNTER_L_ADDR, 2, &T_SLAVE_INTERFACE(stepCounterDataBuffer));
            }
        }

        if (T(sensors[SIGN_MOTION].enabled)) {
            if (T_SLAVE_INTERFACE(funcSrcBuffer[1]) & LSM6DSM_FUNC_SRC_SIGN_MOTION) {
                osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_SIG_MOTION), NULL, NULL);
                DEBUG_PRINT("Significant Motion event!\n");
            }
        }

#ifdef LSM6DSM_I2C_MASTER_ENABLED
        if (T(masterConfigRegister) & LSM6DSM_MASTER_CONFIG_MASTER_ON) {
            T(statusRegisterSH) = T_SLAVE_INTERFACE(statusRegBuffer[1]) & LSM6DSM_FUNC_SRC_SENSOR_HUB_END_OP;
            if (T(statusRegisterSH))
                SPI_READ(LSM6DSM_SENSORHUB1_REG_ADDR, LSM6DSM_SH_READ_BYTE_NUM, &T_SLAVE_INTERFACE(SHDataBuffer));
        }
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

#if defined(LSM6DSM_GYRO_CALIB_ENABLED) || defined(LSM6DSM_ACCEL_CALIB_ENABLED)
        T(statusRegisterTDA) = T_SLAVE_INTERFACE(statusRegBuffer[1]) & LSM6DSM_STATUS_REG_TDA;
        if (T(statusRegisterTDA))
            SPI_READ(LSM6DSM_OUT_TEMP_L_ADDR, LSM6DSM_TEMP_SAMPLE_BYTE, &T_SLAVE_INTERFACE(tempDataBuffer));
#endif /* LSM6DSM_GYRO_CALIB_ENABLED, LSM6DSM_ACCEL_CALIB_ENABLED */

        T(statusRegisterDA) = T_SLAVE_INTERFACE(statusRegBuffer[1]) & (LSM6DSM_STATUS_REG_XLDA | LSM6DSM_STATUS_REG_GDA);

        switch(T(statusRegisterDA)) {
        case LSM6DSM_STATUS_REG_XLDA:
            SPI_READ(LSM6DSM_OUTX_L_XL_ADDR, LSM6DSM_ONE_SAMPLE_BYTE, &T_SLAVE_INTERFACE(accelDataBuffer));
            break;

        case LSM6DSM_STATUS_REG_GDA:
            SPI_READ(LSM6DSM_OUTX_L_G_ADDR, LSM6DSM_ONE_SAMPLE_BYTE, &T_SLAVE_INTERFACE(gyroDataBuffer));
            break;

        case (LSM6DSM_STATUS_REG_XLDA | LSM6DSM_STATUS_REG_GDA):
            SPI_READ(LSM6DSM_OUTX_L_XL_ADDR, LSM6DSM_ONE_SAMPLE_BYTE, &T_SLAVE_INTERFACE(accelDataBuffer));
            SPI_READ(LSM6DSM_OUTX_L_G_ADDR, LSM6DSM_ONE_SAMPLE_BYTE, &T_SLAVE_INTERFACE(gyroDataBuffer));
            break;

        default:
            if (!T(readSteps)) {
                SET_STATE(SENSOR_IDLE);
                lsm6dsm_processPendingEvt();
                return;
            }
            break;
        }

        SET_STATE(SENSOR_INT1_OUTPUT_DATA_HANDLING);
        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &mTask, __FUNCTION__);
        break;

    case SENSOR_INT1_OUTPUT_DATA_HANDLING:
        if (T(readSteps)) {
            union EmbeddedDataPoint step_cnt;

            step_cnt.idata = T_SLAVE_INTERFACE(stepCounterDataBuffer[1]) | (T_SLAVE_INTERFACE(stepCounterDataBuffer[2]) << 8);
            osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_STEP_COUNT), step_cnt.vptr, NULL);
            DEBUG_PRINT("Step Counter update: %ld steps\n", step_cnt.idata);
            T(totalNumSteps) = step_cnt.idata;
            T(readSteps) = false;
        }

#if defined(LSM6DSM_GYRO_CALIB_ENABLED) || defined(LSM6DSM_ACCEL_CALIB_ENABLED)
        if (T(statusRegisterTDA)) {
            T(currentTemperature) = LSM6DSM_TEMP_OFFSET +
                (float)((int16_t)((T_SLAVE_INTERFACE(tempDataBuffer[2]) << 8) | T_SLAVE_INTERFACE(tempDataBuffer[1]))) / 256.0f;
        }
#endif /* LSM6DSM_GYRO_CALIB_ENABLED, LSM6DSM_ACCEL_CALIB_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_ENABLED
        if (T(statusRegisterSH)) {
            T(statusRegisterSH) = 0;
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
            if (T(sensors[MAGN].enabled)) {
                validData = lsm6dsm_processSensorThreeAxisData(&T(sensors[MAGN]), &T_SLAVE_INTERFACE(SHDataBuffer[1]));
                if (validData) {
                    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_MAG), T(sensors[MAGN]).tADataEvt, NULL);

#ifdef LSM6DSM_MAGN_CALIB_ENABLED
                    if T(newMagnCalibData) {
                        magCalGetBias(&T(magnCal), &T(magnCalDataEvt)->samples[0].x,
                            &T(magnCalDataEvt)->samples[0].y, &T(magnCalDataEvt)->samples[0].z);

                        T(newMagnCalibData) = false;
                        T(magnCalDataEvt)->referenceTime = T(timestampInt[LSM6DSM_INT1_INDEX]);
                        osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_MAG_BIAS), T(magnCalDataEvt), NULL);
                    }
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */
                }
            }
#endif /* LSM6DSM_MAGN_CALIB_MAGNETOMETER_ENABLED */

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
            if (T(sensors[PRESS].enabled)) {
                validData = lsm6dsm_processSensorOneAxisData(&T(sensors[PRESS]),
                        &T_SLAVE_INTERFACE(SHDataBuffer[LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN + 1]), T(sensors[PRESS]).sADataEvt);
                if (validData)
                    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_BARO), T(sensors[PRESS]).sADataEvt, NULL);
            }

            if (T(sensors[TEMP].enabled)) {
                union EmbeddedDataPoint tempData;

                validData = lsm6dsm_processSensorOneAxisData(&T(sensors[TEMP]),
                        &T_SLAVE_INTERFACE(SHDataBuffer[LSM6DSM_SENSOR_SLAVE_MAGN_OUTDATA_LEN + LSM6DSM_PRESS_OUTDATA_LEN + 1]), &tempData);
                if (validData)
                    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_TEMP), tempData.vptr, NULL);
            }
#endif /* LSM6DSM_MAGN_CALIB_BAROMETER_ENABLED */
        }
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

        if (T(statusRegisterDA) & LSM6DSM_STATUS_REG_XLDA) {
            validData = lsm6dsm_processSensorThreeAxisData(&T(sensors[ACCEL]), &T_SLAVE_INTERFACE(accelDataBuffer[1]));
            if (validData) {
                if (T(sensors[ACCEL].enabled)) {
                    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ACCEL), T(sensors[ACCEL]).tADataEvt, NULL);
                } else {
#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
                    if (accelCalUpdateBias(&T(accelCal), &T(accelBiasDataEvt)->samples[0].x,
                                &T(accelBiasDataEvt)->samples[0].y, &T(accelBiasDataEvt)->samples[0].z)) {
                        T(accelBiasDataEvt)->referenceTime = T(timestampInt[LSM6DSM_INT1_INDEX]);
                        osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ACCEL_BIAS), T(accelBiasDataEvt), NULL);
                    }
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */
                }
            }
        }

        if (T(statusRegisterDA) & LSM6DSM_STATUS_REG_GDA) {
            validData = lsm6dsm_processSensorThreeAxisData(&T(sensors[GYRO]), &T_SLAVE_INTERFACE(gyroDataBuffer[1]));
            if (validData) {
                osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_GYRO), T(sensors[GYRO]).tADataEvt, NULL);

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
                if (gyroCalNewBiasAvailable(&T(gyroCal))) {
                    gyroCalGetBias(&T(gyroCal), &T(gyroBiasDataEvt)->samples[0].x,
                        &T(gyroBiasDataEvt)->samples[0].y, &T(gyroBiasDataEvt)->samples[0].z);

                    T(gyroBiasDataEvt)->referenceTime = T(timestampInt[LSM6DSM_INT1_INDEX]);
                    osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_GYRO_BIAS), T(gyroBiasDataEvt), NULL);
                }
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
            }
        }

        returnIdle = true;
        break;

    default:
        break;
    }

    if (returnIdle) {
        SET_STATE(SENSOR_IDLE);
        lsm6dsm_processPendingEvt();
    }
}

/*
 * lsm6dsm_handleEvent: handle driver events
 */
static void lsm6dsm_handleEvent(uint32_t evtType, const void *evtData)
{
    TDECL();
    uint64_t currTime;

    switch (evtType) {
    case EVT_APP_START:
        T(mRetryLeft) = LSM6DSM_RETRY_CNT_WAI;
        SET_STATE(SENSOR_BOOT);
        osEventUnsubscribe(T(tid), EVT_APP_START);

        /* Sensor need 100ms to boot, use a timer callback to continue */
        currTime = timGetTime();
        if (currTime < 100000000ULL) {
            timTimerSet(100000000 - currTime, 100, 100, lsm6dsm_timerCallback, NULL, true);
            break;
        }

        /* If 100ms already passed, just fall through next step */
    case EVT_SPI_DONE:
        lsm6dsm_handleSpiDoneEvt(evtData);
        break;

    case EVT_SENSOR_INTERRUPT_1:
        lsm6dsm_readStatusReg(false);
        break;

    case EVT_APP_FROM_HOST:
        break;

    default:
        break;
    }
}

/*
 * lsm6dsm_initSensorStruct: initialize sensor struct variable
 */
static void lsm6dsm_initSensorStruct(struct LSM6DSMSensor *sensor, enum SensorIndex idx)
{
    sensor->idx = idx;
    sensor->rate = 0;
    sensor->hwRate = 0;
    sensor->latency = 0;
    sensor->enabled = false;
    sensor->samplesToDiscard = 0;
    sensor->samplesDecimator = 1;
    sensor->samplesCounter = 0;
    sensor->tADataEvt = NULL;
    sensor->sADataEvt = NULL;
}

/*
 * lsm6dsm_calculateSlabNumItems: calculate number of items needed to allocate memory
 */
static uint8_t lsm6dsm_calculateSlabNumItems()
{
    uint8_t slabElements = 2;

#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
    slabElements++;
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */
#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    slabElements++;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
    slabElements++;
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    slabElements++;
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */

    return slabElements;
}

/*
 * lsm6dsm_startTask: first function executed when App start
 */
static bool lsm6dsm_startTask(uint32_t task_id)
{
    TDECL();
    enum SensorIndex i;
    int err;

    DEBUG_PRINT("IMU: %lu\n", task_id);

    T(tid) = task_id;
    T(int1) = gpioRequest(LSM6DSM_INT1_GPIO);
    T(isr1).func = lsm6dsm_isr1;

    T_SLAVE_INTERFACE(mode).speed = LSM6DSM_SPI_SLAVE_FREQUENCY_HZ;
    T_SLAVE_INTERFACE(mode).bitsPerWord = 8;
    T_SLAVE_INTERFACE(mode).cpol = SPI_CPOL_IDLE_HI;
    T_SLAVE_INTERFACE(mode).cpha = SPI_CPHA_TRAILING_EDGE;
    T_SLAVE_INTERFACE(mode).nssChange = true;
    T_SLAVE_INTERFACE(mode).format = SPI_FORMAT_MSB_FIRST;
    T_SLAVE_INTERFACE(cs) = LSM6DSM_SPI_SLAVE_CS_GPIO;

    DEBUG_PRINT("Requested SPI on bus #%d @ %dHz, int1 on gpio#%d\n",
            LSM6DSM_SPI_SLAVE_BUS_ID, LSM6DSM_SPI_SLAVE_FREQUENCY_HZ, LSM6DSM_INT1_GPIO);

    err = spiMasterRequest(LSM6DSM_SPI_SLAVE_BUS_ID, &T_SLAVE_INTERFACE(spiDev));
    if (err < 0) {
        ERROR_PRINT("Failed to request SPI on this bus: #%d\n", LSM6DSM_SPI_SLAVE_BUS_ID);
        return false;
    }

    T(int1Register) = LSM6DSM_INT1_CTRL_BASE;
    T(int2Register) = LSM6DSM_INT2_CTRL_BASE;
    T(embeddedFunctionsRegister) = LSM6DSM_CTRL10_C_BASE;
    T(accelSensorDependencies) = 0;
    T(embeddedFunctionsDependencies) = 0;
    T(pendingInt[LSM6DSM_INT1_INDEX]) = false;
    T(pendingInt[LSM6DSM_INT2_INDEX]) = false;
    T(timestampInt[LSM6DSM_INT1_INDEX]) = 0;
    T(timestampInt[LSM6DSM_INT2_INDEX]) = 0;
    T(totalNumSteps) = 0;
    T(triggerRate) = SENSOR_HZ_RATE_TO_US(SENSOR_HZ(26.0f / 2.0f));
#if defined(LSM6DSM_GYRO_CALIB_ENABLED) || defined(LSM6DSM_ACCEL_CALIB_ENABLED)
    T(currentTemperature) = 0;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED, LSM6DSM_ACCEL_CALIB_ENABLED */
#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    T(newMagnCalibData) = false;
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_ENABLED
    T(masterConfigRegister) = LSM6DSM_MASTER_CONFIG_BASE;
#endif /* LSM6DSM_I2C_MASTER_ENABLED */

    T(mDataSlabThreeAxis) = slabAllocatorNew(sizeof(struct TripleAxisDataEvent) + sizeof(struct TripleAxisDataPoint), 4, lsm6dsm_calculateSlabNumItems());
    if (!T(mDataSlabThreeAxis)) {
        ERROR_PRINT("Failed to allocate mDataSlabThreeAxis memory\n");
        spiMasterRelease(T_SLAVE_INTERFACE(spiDev));
        return false;
    }

#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    T(mDataSlabOneAxis) = slabAllocatorNew(sizeof(struct SingleAxisDataEvent) + sizeof(struct SingleAxisDataPoint), 4, 10);
    if (!T(mDataSlabOneAxis)) {
        ERROR_PRINT("Failed to allocate mDataSlabOneAxis memory\n");
        slabAllocatorDestroy(T(mDataSlabThreeAxis));
        spiMasterRelease(T_SLAVE_INTERFACE(spiDev));
        return false;
    }
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
    T(accelBiasDataEvt) = slabAllocatorAlloc(T(mDataSlabThreeAxis));
    if (!T(accelBiasDataEvt)) {
        ERROR_PRINT("Failed to allocate accelBiasDataEvt memory");
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        slabAllocatorDestroy(T(mDataSlabOneAxis));
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
        slabAllocatorDestroy(T(mDataSlabThreeAxis));
        spiMasterRelease(T_SLAVE_INTERFACE(spiDev));
        return false;
    }

    memset(&T(accelBiasDataEvt)->samples[0].firstSample, 0, sizeof(struct SensorFirstSample));
    T(accelBiasDataEvt)->samples[0].firstSample.biasCurrent = true;
    T(accelBiasDataEvt)->samples[0].firstSample.biasPresent = 1;
    T(accelBiasDataEvt)->samples[0].firstSample.biasSample = 0;
    T(accelBiasDataEvt)->samples[0].firstSample.numSamples = 1;
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    T(gyroBiasDataEvt) = slabAllocatorAlloc(T(mDataSlabThreeAxis));
    if (!T(gyroBiasDataEvt)) {
        ERROR_PRINT("Failed to allocate gyroBiasDataEvt memory");
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        slabAllocatorDestroy(T(mDataSlabOneAxis));
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
        slabAllocatorDestroy(T(mDataSlabThreeAxis));
        spiMasterRelease(T_SLAVE_INTERFACE(spiDev));
        return false;
    }

    memset(&T(gyroBiasDataEvt)->samples[0].firstSample, 0, sizeof(struct SensorFirstSample));
    T(gyroBiasDataEvt)->samples[0].firstSample.biasCurrent = true;
    T(gyroBiasDataEvt)->samples[0].firstSample.biasPresent = 1;
    T(gyroBiasDataEvt)->samples[0].firstSample.biasSample = 0;
    T(gyroBiasDataEvt)->samples[0].firstSample.numSamples = 1;
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    T(magnCalDataEvt) = slabAllocatorAlloc(T(mDataSlabThreeAxis));
    if (!T(magnCalDataEvt)) {
        ERROR_PRINT("Failed to allocate magnCalDataEvt memory");
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        slabAllocatorDestroy(T(mDataSlabOneAxis));
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
        slabAllocatorDestroy(T(mDataSlabThreeAxis));
        spiMasterRelease(T_SLAVE_INTERFACE(spiDev));
        return false;
    }

    memset(&T(magnCalDataEvt)->samples[0].firstSample, 0, sizeof(struct SensorFirstSample));
    T(magnCalDataEvt)->samples[0].firstSample.biasCurrent = true;
    T(magnCalDataEvt)->samples[0].firstSample.biasPresent = 1;
    T(magnCalDataEvt)->samples[0].firstSample.biasSample = 0;
    T(magnCalDataEvt)->samples[0].firstSample.numSamples = 1;
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */

    for (i = 0; i < NUM_SENSORS; i++) {
        T(pendingEnableConfig[i]) = false;
        T(pendingRateConfig[i]) = false;
        lsm6dsm_initSensorStruct(&T(sensors[i]), i);

#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
        if ((i == ACCEL) || (i == GYRO) || (i == MAGN)) {
#else /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
        if ((i == ACCEL) || (i == GYRO)) {
#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
            T(sensors[i]).tADataEvt = slabAllocatorAlloc(T(mDataSlabThreeAxis));
            if (!T(sensors[i]).tADataEvt) {
                ERROR_PRINT("Failed to allocate tADataEvt memory");
                goto unregister_sensors;
            }

            memset(&T(sensors[i]).tADataEvt->samples[0].firstSample, 0, sizeof(struct SensorFirstSample));
        }
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
        if (i == PRESS) {
            T(sensors[i]).sADataEvt = slabAllocatorAlloc(T(mDataSlabOneAxis));
            if (!T(sensors[i]).sADataEvt) {
                ERROR_PRINT("Failed to allocate sADataEvt memory");
                goto unregister_sensors;
            }

            memset(&T(sensors[i]).sADataEvt->samples[0].firstSample, 0, sizeof(struct SensorFirstSample));
        }
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */

        T(sensors[i]).handle = sensorRegister(&LSM6DSMSensorInfo[i], &LSM6DSMSensorOps[i], NULL, false);
    }

#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
    accelCalInit(&T(accelCal),
            800000000,         /* stillness period = 0.8 seconds [ns] */
            5,                 /* minimum sample number */
            0.00025,           /* threshold */
            15,                /* nx bucket count */
            15,                /* nxb bucket count */
            15,                /* ny bucket count */
            15,                /* nyb bucket count */
            15,                /* nz bucket count */
            15,                /* nzb bucket count */
            15);               /* nle bucket count */
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */

#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    gyroCalInit(&T(gyroCal),
            5e9,               /* min stillness period = 5 seconds [ns] */
            6e9,               /* max stillness period = 6 seconds [ns] */
            0, 0, 0,           /* initial bias offset calibration values [rad/sec] */
            0,                 /* timestamp of initial bias calibration [ns] */
            1.5e9,             /* analysis window length = 1.5 seconds [ns] */
            5e-5f,             /* gyroscope variance threshold [rad/sec]^2 */
            1e-5f,             /* gyroscope confidence delta [rad/sec]^2 */
            8e-3f,             /* accelerometer variance threshold [m/sec^2]^2 */
            1.6e-3f,           /* accelerometer confidence delta [m/sec^2]^2 */
            1.4f,              /* magnetometer variance threshold [uT]^2 */
            0.25,              /* magnetometer confidence delta [uT]^2 */
            0.95f,             /* stillness threshold [0, 1] */
            1);                /* 1 : gyro calibrations will be applied */
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    initMagCal(&T(magnCal),
            0.0f, 0.0f, 0.0f,  /* magn offset x - y - z */
            1.0f, 0.0f, 0.0f,  /* magn scale matrix c00 - c01 - c02 */
            0.0f, 1.0f, 0.0f,  /* magn scale matrix c10 - c11 - c12 */
            0.0f, 0.0f, 1.0f); /* magn scale matrix c20 - c21 - c22 */
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */

    /* Initialize index used to fill/get data from buffer */
    T_SLAVE_INTERFACE(mWbufCnt) = 0;
    T_SLAVE_INTERFACE(mRegCnt) = 0;

    osEventSubscribe(T(tid), EVT_APP_START);

    DEBUG_PRINT("Enabling gpio#%d connected to int1\n", LSM6DSM_INT1_GPIO);
    lsm6dsm_enableInterrupt(T(int1), &T(isr1));

    return true;

unregister_sensors:
    for (i--; i >= ACCEL; i--)
        sensorUnregister(T(sensors[i]).handle);

#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
    accelCalDestroy(&T(accelCal));
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */
#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    destroy_mag_cal(&T(magnCal));
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */
#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    gyroCalDestroy(&T(gyroCal));
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    slabAllocatorDestroy(T(mDataSlabOneAxis));
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
    slabAllocatorDestroy(T(mDataSlabThreeAxis));
    spiMasterRelease(T_SLAVE_INTERFACE(spiDev));
    return false;
}

/*
 * lsm6dsm_endTask: last function executed when App end
 */
static void lsm6dsm_endTask(void)
{
    TDECL();
    enum SensorIndex i;

#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
    accelCalDestroy(&T(accelCal));
#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */
#ifdef LSM6DSM_MAGN_CALIB_ENABLED
    destroy_mag_cal(&T(magnCal));
#endif /* LSM6DSM_MAGN_CALIB_ENABLED */
#ifdef LSM6DSM_GYRO_CALIB_ENABLED
    gyroCalDestroy(&T(gyroCal));
#endif /* LSM6DSM_GYRO_CALIB_ENABLED */

    lsm6dsm_disableInterrupt(T(int1), &T(isr1));
#ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
    slabAllocatorDestroy(T(mDataSlabOneAxis));
#endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
    slabAllocatorDestroy(T(mDataSlabThreeAxis));
    spiMasterRelease(T_SLAVE_INTERFACE(spiDev));

    for (i = 0; i < NUM_SENSORS; i++)
        sensorUnregister(T(sensors[i]).handle);

    gpioRelease(T(int1));
}

INTERNAL_APP_INIT(LSM6DSM_APP_ID, 0, lsm6dsm_startTask, lsm6dsm_endTask, lsm6dsm_handleEvent);