nanohub:drivers:st_lsm6dsm: added sensorCfgData and Calibration functions

This patch will allow driver to receive configuration data from HAL and be
able to perform `factory` calibration using nanotool -x calibrate -s sensorName.

Change-Id: I6316fd64d97b1ff10ca0242f09d3dec0696d1ac2
Signed-off-by: Denis Ciocca <denis.ciocca@st.com>
diff --git a/firmware/os/drivers/st_lsm6dsm/st_lsm6dsm.c b/firmware/os/drivers/st_lsm6dsm/st_lsm6dsm.c
index c911e3d..cddeb33 100644
--- a/firmware/os/drivers/st_lsm6dsm/st_lsm6dsm.c
+++ b/firmware/os/drivers/st_lsm6dsm/st_lsm6dsm.c
@@ -172,6 +172,7 @@
 #define LSM6DSM_STEP_COUNTER_L_ADDR                     (0x4b)
 #define LSM6DSM_FUNC_SRC_ADDR                           (0x53)
 #define LSM6DSM_WAKE_UP_DUR_ADDR                        (0x5c)
+#define LSM6DSM_X_OFS_USR_ADDR                          (0x73)
 
 #define LSM6DSM_SW_RESET                                (0x01)
 #define LSM6DSM_RESET_PEDOMETER                         (0x02)
@@ -211,6 +212,12 @@
 #define LSM6DSM_GYRO_SELFTEST_HIGH_THR_LSB              10000           /* 700dps @2000dps in LSB */
 #define LSM6DSM_GYRO_SELFTEST_LOW_THR_LSB               2142            /* 150dps @2000dps in LSB */
 
+/* LSM6DSM calibration related */
+#define LSM6DSM_NUM_AVERAGE_CALIBRATION                 10
+#define LSM6DSM_1G_IN_LSB_CALIBRATION                   4098            /* 1000mg @8g in LSB */
+#define LSM6DSM_ACCEL_MAX_CALIBRATION_THR_LSB           127             /* 8-bit available */
+#define LSM6DSM_ACCEL_LSB_TO_OFFSET_DIGIT_SCALE         0.2501f         /* @8g */
+
 /* LSM6DSM embedded registers */
 #define LSM6DSM_EMBEDDED_SLV0_ADDR_ADDR                 (0x02)
 #define LSM6DSM_EMBEDDED_SLV0_SUBADDR_ADDR              (0x03)
@@ -494,6 +501,13 @@
     SELFTEST_COMPLETED
 };
 
+enum CalibrationState {
+    CALIBRATION_INITIALIZATION = 0,
+    CALIBRATION_READ_DATA,
+    CALIBRATION_VERIFICATION,
+    CALIBRATION_COMPLETED
+};
+
 enum SensorEvents {
     NO_EVT = -1,
     EVT_SPI_DONE = EVT_APP_START + 1,
@@ -502,6 +516,7 @@
     EVT_SENSOR_POWERING_UP,
     EVT_SENSOR_POWERING_DOWN,
     EVT_SENSOR_CONFIG_CHANGING,
+    EVT_SENSOR_RESTORE_IDLE,
     EVT_TIME_SYNC
 };
 
@@ -514,6 +529,8 @@
     SENSOR_POWERING_DOWN,
     SENSOR_CONFIG_CHANGING,
     SENSOR_CONFIG_WATERMARK_CHANGING,
+    SENSOR_CALIBRATION,
+    SENSOR_STORE_CALIBRATION_DATA,
     SENSOR_SELFTEST,
     SENSOR_INT1_STATUS_REG_HANDLING,
     SENSOR_INT1_OUTPUT_DATA_HANDLING,
@@ -748,7 +765,8 @@
     bool timestampIsValid;
 };
 
-/* LSM6DSMSelfTestResultPkt: calibration packet result data
+/*
+ * struct LSM6DSMSelfTestResultPkt: self-test packet result data
  * @header: describe packet size and application ID of packet.
  * @dataHeader: payload of message.
  */
@@ -757,7 +775,34 @@
     struct SensorAppEventHeader dataHeader;
 } __attribute__((packed));
 
-/* struct LSM6DSMTask: driver task data
+/*
+ * struct LSM6DSMCalibrationResultPkt: calibration packet result data
+ * @header: describe packet size and application ID of packet.
+ * @dataHeader: payload of header message.
+ * @xBias: raw offset value X axis.
+ * @yBias: raw offset value Y axis.
+ * @zBias: raw offset value Z axis.
+ */
+struct LSM6DSMCalibrationResultPkt {
+    struct HostHubRawPacket header;
+    struct SensorAppEventHeader dataHeader;
+    int32_t xBias;
+    int32_t yBias;
+    int32_t zBias;
+} __attribute__((packed));
+
+/*
+ * struct LSM6DSMAccelGyroCfgData: configuration packet data
+ * @hw: chip level calibration data.
+ * @sw: software level calibration data (algos).
+ */
+struct LSM6DSMAccelGyroCfgData {
+    int32_t hw[3];
+    float sw[3];
+};
+
+/*
+ * struct LSM6DSMTask: driver task data
  * @sensors: sensor status data list.
  * @slaveConn: slave interface / communication data.
  * @accelCal: accelerometer calibration algo data.
@@ -773,12 +818,21 @@
  * @currentTemperature: sensor temperature data value used by gyroscope/accelerometer bias calibration libs.
  * @lastFifoReadTimestamp: store when last time FIFO was read.
  * @initState: initialization is done in several steps (enum InitState).
+ * @selftestState: self-test is performed in several steps (enum SelfTestState).
+ * @calibrationState: sensor calibration is done in several steps (enum CalibrationState).
  * @tid: task id.
  * @totalNumSteps: total number of steps of step counter sensor.
  * @fifoDataToRead: number of byte to read in current FIFO read.
  * @fifoDataToReadPending: in order to reduce txrxBuffer, FIFO read is performed in several read. This value tell how many data still need to read from FIFO.
  * @baroTimerId: barometer task timer id.
+ * @dataSelftestEnabled: sensor data read during GapSelfTestProgram while self-test bit is set.
+ * @dataSelftestNotEnabled: sensor data read during GapSelfTestProgram while self-test bit is not set.
+ * @dataCalibration: sensor data read during calibration program.
+ * @accelCalibrationData: accelerometer offset value (hw) to store into sensor.
+ * @gyroCalibrationData: gyroscope offset value (hw) applied to each sample (by software).
  * @state: task state, driver manage operations using a state machine (enum SensorState).
+ * @numSamplesSelftest: temp variable storing number of samples read by self-test program.
+ * @numSamplesCalibration: temp variable storing number of samples read by calibration program.
  * @mRetryLeft: counter used to retry operations #n times before return a failure.
  * @pedometerDependencies: dependencies mask of sensors that are using embedded functions.
  * @masterConfigDependencies: dependencies mask of sensors that are using I2C master.
@@ -794,6 +848,7 @@
  * @pendingInt: pending interrupt task to be executed.
  * @pendingTimeSyncTask: pending time sync task to be executed.
  * @pendingBaroTimerTask: pending baro read data task to be executed.
+ * @pendingStoreAccelCalibData: pending calibration data store task to be executed.
  */
 typedef struct LSM6DSMTask {
     struct LSM6DSMSensor sensors[NUM_SENSORS];
@@ -829,6 +884,7 @@
 
     enum InitState initState;
     enum SelfTestState selftestState;
+    enum CalibrationState calibrationState;
 
     uint32_t tid;
     uint32_t totalNumSteps;
@@ -839,10 +895,14 @@
 #endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
     int32_t dataSelftestEnabled[3];
     int32_t dataSelftestNotEnabled[3];
+    int32_t dataCalibration[3];
+    int32_t accelCalibrationData[3];
+    int32_t gyroCalibrationData[3];
 
     volatile uint8_t state;
 
     uint8_t numSamplesSelftest;
+    uint8_t numSamplesCalibration;
     uint8_t mRetryLeft;
     uint8_t pedometerDependencies;
     uint8_t masterConfigDependencies;
@@ -863,6 +923,7 @@
 #if defined(LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED) && defined(LSM6DSM_I2C_MASTER_BAROMETER_ENABLED)
     bool pendingBaroTimerTask;
 #endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
+    bool pendingStoreAccelCalibData;
 } LSM6DSMTask;
 
 static LSM6DSMTask mTask;
@@ -923,7 +984,8 @@
     .flags1 = SENSOR_INFO_FLAGS1_RAW | SENSOR_INFO_FLAGS1_BIAS, \
     .biasType = bias
 
-/* LSM6DSMImuRates: supported frequencies by accelerometer and gyroscope sensors
+/*
+ * LSM6DSMImuRates: supported frequencies by accelerometer and gyroscope sensors
  * LSM6DSMImuRatesRegValue, LSM6DSMRatesSamplesToDiscardGyroPowerOn, LSM6DSMAccelRatesSamplesToDiscard,
  *     LSM6DSMGyroRatesSamplesToDiscard must have same length.
  */
@@ -1095,8 +1157,15 @@
     .sensorFlush = flush, \
     .sensorSendOneDirectEvt = send
 
-#define DEC_OPS_SELFTEST(power, firmware, rate, flush, selftest) \
+#define DEC_OPS_CFG_SELFTEST(power, firmware, rate, flush, cfgData, selftest) \
     DEC_OPS(power, firmware, rate, flush), \
+    .sensorCfgData = cfgData, \
+    .sensorSelfTest = selftest
+
+#define DEC_OPS_CAL_CFG_SELFTEST(power, firmware, rate, flush, cal, cfgData, selftest) \
+    DEC_OPS(power, firmware, rate, flush), \
+    .sensorCalibrate = cal, \
+    .sensorCfgData = cfgData, \
     .sensorSelfTest = selftest
 
 static bool lsm6dsm_setAccelPower(bool on, void *cookie);
@@ -1122,15 +1191,18 @@
 static bool lsm6dsm_stepCounterSendLastData(void *cookie, uint32_t tid);
 static bool lsm6dsm_runAccelSelfTest(void *cookie);
 static bool lsm6dsm_runGyroSelfTest(void *cookie);
-#ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
-static bool lsm6dsm_runMagnSelfTest(void *cookie);
-#endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
+static bool lsm6dsm_runAccelCalibration(void *cookie);
+static bool lsm6dsm_runGyroCalibration(void *cookie);
+static bool lsm6dsm_accelCfgData(void *data, void *cookie);
+static bool lsm6dsm_gyroCfgData(void *data, void *cookie);
 
 #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);
+static bool lsm6dsm_runMagnSelfTest(void *cookie);
+static bool lsm6dsm_magnCfgData(void *data, void *cookie);
 #endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
 
 #ifdef LSM6DSM_I2C_MASTER_BAROMETER_ENABLED
@@ -1145,17 +1217,21 @@
 #endif /* LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
 
 static const struct SensorOps LSM6DSMSensorOps[NUM_SENSORS] = {
-    { DEC_OPS_SELFTEST(lsm6dsm_setGyroPower, lsm6dsm_gyroFirmwareUpload, lsm6dsm_setGyroRate, lsm6dsm_gyroFlush, lsm6dsm_runGyroSelfTest) },
-    { DEC_OPS_SELFTEST(lsm6dsm_setAccelPower, lsm6dsm_accelFirmwareUpload, lsm6dsm_setAccelRate, lsm6dsm_accelFlush, lsm6dsm_runAccelSelfTest) },
+    { DEC_OPS_CAL_CFG_SELFTEST(lsm6dsm_setGyroPower, lsm6dsm_gyroFirmwareUpload, lsm6dsm_setGyroRate,
+                                lsm6dsm_gyroFlush, lsm6dsm_runGyroCalibration, lsm6dsm_gyroCfgData, lsm6dsm_runGyroSelfTest) },
+    { DEC_OPS_CAL_CFG_SELFTEST(lsm6dsm_setAccelPower, lsm6dsm_accelFirmwareUpload, lsm6dsm_setAccelRate,
+                                lsm6dsm_accelFlush, lsm6dsm_runAccelCalibration, lsm6dsm_accelCfgData, lsm6dsm_runAccelSelfTest) },
 #ifdef LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED
-    { DEC_OPS_SELFTEST(lsm6dsm_setMagnPower, lsm6dsm_magnFirmwareUpload, lsm6dsm_setMagnRate, lsm6dsm_magnFlush, lsm6dsm_runMagnSelfTest) },
+    { DEC_OPS_CFG_SELFTEST(lsm6dsm_setMagnPower, lsm6dsm_magnFirmwareUpload, lsm6dsm_setMagnRate,
+                                lsm6dsm_magnFlush, lsm6dsm_magnCfgData, lsm6dsm_runMagnSelfTest) },
 #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_SEND(lsm6dsm_setStepCounterPower, lsm6dsm_stepCounterFirmwareUpload, lsm6dsm_setStepCounterRate,
+                                lsm6dsm_stepCounterFlush, lsm6dsm_stepCounterSendLastData) },
     { DEC_OPS(lsm6dsm_setSignMotionPower, lsm6dsm_signMotionFirmwareUpload, lsm6dsm_setSignMotionRate, lsm6dsm_signMotionFlush) },
 };
 
@@ -1457,6 +1533,40 @@
 }
 
 /*
+ * lsm6dsm_sendCalibrationResult: send to nanohub result of calibration
+ * @sensorType: android sensor type.
+ * @result: status message to send (VALID/ERROR).
+ * @xBias: raw offset value X axis.
+ * @yBias: raw offset value Y axis.
+ * @zBias: raw offset value Z axis.
+ */
+static void lsm6dsm_sendCalibrationResult(uint8_t sensorType, uint8_t result, int32_t xBias, int32_t yBias, int32_t zBias)
+{
+    struct LSM6DSMCalibrationResultPkt *data;
+
+    data = heapAlloc(sizeof(struct LSM6DSMCalibrationResultPkt));
+    if (!data) {
+        ERROR_PRINT("sendCalibrationResult: cannot allocate calibration result packet\n");
+        return;
+    }
+
+    data->header.appId = LSM6DSM_APP_ID;
+    data->header.dataLen = (sizeof(struct LSM6DSMCalibrationResultPkt) - sizeof(struct HostHubRawPacket));
+
+    data->dataHeader.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
+    data->dataHeader.sensorType = sensorType;
+    data->dataHeader.status = result;
+
+    data->xBias = xBias;
+    data->yBias = yBias;
+    data->zBias = zBias;
+
+    if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree)) {
+        ERROR_PRINT("sendCalibrationResult: failed to enqueue calibration result packet\n");
+    }
+}
+
+/*
  * lsm6dsm_runGapSelfTestProgram: state machine that is executing self-test verifying data gap
  * @idx: sensor driver index.
  */
@@ -1713,11 +1823,141 @@
     }
 }
 
+/*
+ * lsm6dsm_convertAccelOffsetValue: convert accel LSB value to offset digit
+ * @val: LSB axis offset value.
+ */
+static uint8_t lsm6dsm_convertAccelOffsetValue(int32_t val)
+{
+    float temp;
+
+    temp = val * LSM6DSM_ACCEL_LSB_TO_OFFSET_DIGIT_SCALE;
+    if (temp > LSM6DSM_ACCEL_MAX_CALIBRATION_THR_LSB)
+        temp = LSM6DSM_ACCEL_MAX_CALIBRATION_THR_LSB;
+
+    if (temp < -LSM6DSM_ACCEL_MAX_CALIBRATION_THR_LSB)
+        temp = -LSM6DSM_ACCEL_MAX_CALIBRATION_THR_LSB;
+
+    return (uint8_t)((int8_t)temp);
+}
+
+/*
+ * lsm6dsm_runCalibrationProgram: state machine that is executing calibration
+ * @idx: sensor driver index.
+ */
+static void lsm6dsm_runCalibrationProgram(enum SensorIndex idx)
+{
+    TDECL();
+    uint8_t *sensorData, numberOfAverage;
+    uint8_t buffer[3] = { 0 };
+
+    numberOfAverage = LSM6DSM_NUM_AVERAGE_CALIBRATION;
+
+    switch (T(calibrationState)) {
+    case CALIBRATION_INITIALIZATION:
+        DEBUG_PRINT("runCalibrationProgram: initialization\n");
+
+        T(numSamplesCalibration) = 0;
+        memset(T(dataCalibration), 0, 3 * sizeof(int32_t));
+
+        /* Power on sensor */
+        switch (idx) {
+        case ACCEL:
+            SPI_MULTIWRITE(LSM6DSM_X_OFS_USR_ADDR, buffer, 3, 500);
+            SPI_WRITE(LSM6DSM_CTRL1_XL_ADDR, LSM6DSM_CTRL1_XL_BASE | LSM6DSM_ODR_104HZ_REG_VALUE, 30000);
+            break;
+        case GYRO:
+            SPI_WRITE(LSM6DSM_CTRL2_G_ADDR, LSM6DSM_CTRL2_G_BASE | LSM6DSM_ODR_104HZ_REG_VALUE, 100000);
+            break;
+        default:
+            return;
+        }
+
+        T(calibrationState) = CALIBRATION_READ_DATA;
+        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[idx]), __FUNCTION__);
+        break;
+
+    case CALIBRATION_READ_DATA:
+        if (T(numSamplesCalibration) > 0) {
+            sensorData = &T_SLAVE_INTERFACE(tmpDataBuffer[1]);
+            T(dataCalibration[0]) += (int16_t)*((uint16_t *)&sensorData[0]);
+            T(dataCalibration[1]) += (int16_t)*((uint16_t *)&sensorData[2]);
+            T(dataCalibration[2]) += (int16_t)*((uint16_t *)&sensorData[4]);
+        }
+        T(numSamplesCalibration)++;
+
+        if (T(numSamplesCalibration) <= numberOfAverage) {
+            DEBUG_PRINT("runCalibrationProgram: reading output data\n");
+
+            switch (idx) {
+            case ACCEL:
+                SPI_READ(LSM6DSM_OUTX_L_XL_ADDR, LSM6DSM_ONE_SAMPLE_BYTE, &T_SLAVE_INTERFACE(tmpDataBuffer), 10000);
+                break;
+            case GYRO:
+                SPI_READ(LSM6DSM_OUTX_L_G_ADDR, LSM6DSM_ONE_SAMPLE_BYTE, &T_SLAVE_INTERFACE(tmpDataBuffer), 10000);
+                break;
+            default:
+                return;
+            }
+            lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[idx]), __FUNCTION__);
+            break;
+        }
+
+        T(dataCalibration[0]) /= numberOfAverage;
+        T(dataCalibration[1]) /= numberOfAverage;
+        T(dataCalibration[2]) /= numberOfAverage;
+        T(calibrationState) = CALIBRATION_VERIFICATION;
+
+    case CALIBRATION_VERIFICATION: ;
+        uint8_t sType;
+
+        switch (idx) {
+        case ACCEL:
+            sType = SENS_TYPE_ACCEL;
+
+            /* Power off sensor */
+            SPI_WRITE(LSM6DSM_CTRL1_XL_ADDR, LSM6DSM_CTRL1_XL_BASE);
+
+            /* Supposed 0,0,1g (Android coordinate system) */
+            T(dataCalibration[0]) = -T(dataCalibration[0]);
+            T(dataCalibration[1]) = -T(dataCalibration[1]);
+            T(dataCalibration[2]) = T(dataCalibration[2]) - LSM6DSM_1G_IN_LSB_CALIBRATION;
+
+            for (int8_t i = 0; i < 3; i++)
+                buffer[i] = lsm6dsm_convertAccelOffsetValue(T(dataCalibration[i]));
+
+            SPI_MULTIWRITE(LSM6DSM_X_OFS_USR_ADDR, buffer, 3);
+            break;
+        case GYRO:
+            sType = SENS_TYPE_GYRO;
+
+            /* Power off sensor */
+            SPI_WRITE(LSM6DSM_CTRL2_G_ADDR, LSM6DSM_CTRL2_G_BASE);
+
+            memcpy(T(gyroCalibrationData), T(dataCalibration), 3 * sizeof(int32_t));
+            break;
+        default:
+            return;
+        }
+
+        INFO_PRINT("runCalibrationProgram: completed. offset [LSB]: %ld %ld %ld\n", T(dataCalibration[0]), T(dataCalibration[1]), T(dataCalibration[2]));
+        lsm6dsm_sendCalibrationResult(sType, SENSOR_APP_EVT_STATUS_SUCCESS, T(dataCalibration[0]), T(dataCalibration[1]), T(dataCalibration[2]));
+
+        T(calibrationState) = CALIBRATION_COMPLETED;
+        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, &T(sensors[idx]), __FUNCTION__);
+        break;
+
+    default:
+        break;
+    }
+}
+
+
 #ifdef LSM6DSM_I2C_MASTER_AK09916
 /*
  * lsm6dsm_runAbsoluteSelfTestProgram: state machine that is executing self-test verifying absolute value
  */
-static void lsm6dsm_runAbsoluteSelfTestProgram()
+static void lsm6dsm_runAbsoluteSelfTestProgram(void)
 {
     TDECL();
     uint8_t *sensorData;
@@ -2141,7 +2381,7 @@
 /*
  * lsm6dsm_updateOdrs: update ODRs based on rates
  */
-static bool lsm6dsm_updateOdrs()
+static bool lsm6dsm_updateOdrs(void)
 {
     TDECL();
     bool accelOdrChanged = false, gyroOdrChanged = false, decChanged, watermarkChanged, gyroFirstEnable = false;
@@ -3191,7 +3431,7 @@
 
         if (sensorGetTime() <= (T(lastFifoReadTimestamp) + ((uint64_t)lsm6dsm_sensorHzToNs(T(fifoCntl).triggerRate) * T(fifoCntl).maxDecimator))) {
             osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ACCEL), SENSOR_DATA_EVENT_FLUSH, NULL);
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
             return true;
         }
 
@@ -3218,7 +3458,7 @@
 
         if (sensorGetTime() <= (T(lastFifoReadTimestamp) + ((uint64_t)lsm6dsm_sensorHzToNs(T(fifoCntl).triggerRate) * T(fifoCntl).maxDecimator))) {
             osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_GYRO), SENSOR_DATA_EVENT_FLUSH, NULL);
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
             return true;
         }
 
@@ -3246,7 +3486,7 @@
 
         if (sensorGetTime() <= (T(lastFifoReadTimestamp) + ((uint64_t)lsm6dsm_sensorHzToNs(T(fifoCntl).triggerRate) * T(fifoCntl).maxDecimator))) {
             osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_MAG), SENSOR_DATA_EVENT_FLUSH, NULL);
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
             return true;
         }
 
@@ -3276,7 +3516,7 @@
 
         if (sensorGetTime() <= (T(lastFifoReadTimestamp) + ((uint64_t)lsm6dsm_sensorHzToNs(T(fifoCntl).triggerRate) * T(fifoCntl).maxDecimator))) {
             osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_BARO), SENSOR_DATA_EVENT_FLUSH, NULL);
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
             return true;
         }
 
@@ -3309,7 +3549,7 @@
 
         if (sensorGetTime() <= (T(lastFifoReadTimestamp) + ((uint64_t)lsm6dsm_sensorHzToNs(T(fifoCntl).triggerRate) * T(fifoCntl).maxDecimator))) {
             osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_TEMP), SENSOR_DATA_EVENT_FLUSH, NULL);
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
             return true;
         }
 
@@ -3405,7 +3645,7 @@
             lsm6dsm_runGapSelfTestProgram(ACCEL);
             return true;
         } else
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
     }
 
     ERROR_PRINT("runAccelSelfTest: cannot run selftest because sensor is busy!\n");
@@ -3429,7 +3669,7 @@
             lsm6dsm_runGapSelfTestProgram(GYRO);
             return true;
         } else
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
     }
 
     ERROR_PRINT("runGyroSelfTest: cannot run selftest because sensor is busy!\n");
@@ -3458,7 +3698,7 @@
 #endif
             return true;
         } else
-            SET_STATE(SENSOR_IDLE);
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
     }
 
     ERROR_PRINT("runMagnSelfTest: cannot run selftest because sensor is busy!\n");
@@ -3466,9 +3706,142 @@
 
     return false;
 }
+
+/*
+ * lsm6dsm_magnCfgData: set sw magnetometer calibration values
+ * @data: calibration data struct.
+ * @cookie: private data.
+ */
+static bool lsm6dsm_magnCfgData(void *data, void *cookie)
+{
+    TDECL();
+    float *values = data;
+
+    DEBUG_PRINT("Magn sw bias data [uT * 1000]: %ld %ld %ld\n", (int32_t)(values[0] * 1000), (int32_t)(values[1] * 1000), (int32_t)(values[2] * 1000));
+
+    T(magnCal).x_bias = values[0];
+    T(magnCal).y_bias = values[1];
+    T(magnCal).z_bias = values[2];
+
+    return true;
+}
 #endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED */
 
 /*
+ * lsm6dsm_runAccelCalibration: execute accelerometer calibration
+ * @cookie: private data.
+ */
+static bool lsm6dsm_runAccelCalibration(void *cookie)
+{
+    TDECL();
+
+    if (trySwitchState(SENSOR_CALIBRATION)) {
+        if (!T(sensors[ACCEL]).enabled && (T(sensors[ACCEL]).hwRate == 0) && (T(sensors[GYRO]).hwRate == 0)) {
+            INFO_PRINT("runAccelCalibration: executing accelerometer calibration\n");
+            T(calibrationState) = CALIBRATION_INITIALIZATION;
+            lsm6dsm_runCalibrationProgram(ACCEL);
+            return true;
+        } else
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
+    }
+
+    ERROR_PRINT("runAccelCalibration: cannot run selftest because sensor is busy!\n");
+    lsm6dsm_sendCalibrationResult(SENS_TYPE_ACCEL, SENSOR_APP_EVT_STATUS_BUSY, 0, 0, 0);
+
+    return true;
+}
+
+/*
+ * lsm6dsm_runGyroCalibration: execute gyroscope calibration
+ * @cookie: private data.
+ */
+static bool lsm6dsm_runGyroCalibration(void *cookie)
+{
+    TDECL();
+
+    if (trySwitchState(SENSOR_CALIBRATION)) {
+        if (!T(sensors[GYRO]).enabled && (T(sensors[GYRO]).hwRate == 0) && (T(sensors[ACCEL]).hwRate == 0)) {
+            INFO_PRINT("runGyroCalibration: executing gyroscope calibration\n");
+            T(calibrationState) = CALIBRATION_INITIALIZATION;
+            lsm6dsm_runCalibrationProgram(GYRO);
+            return true;
+        } else
+            osEnqueuePrivateEvt(EVT_SENSOR_RESTORE_IDLE, cookie, NULL, mTask.tid);
+    }
+
+    ERROR_PRINT("runGyroCalibration: cannot run selftest because sensor is busy!\n");
+    lsm6dsm_sendCalibrationResult(SENS_TYPE_GYRO, SENSOR_APP_EVT_STATUS_BUSY, 0, 0, 0);
+
+    return true;
+}
+
+/*
+ * lsm6dsm_storeAccelCalibrationData: store hw calibration into sensor
+ */
+static bool lsm6dsm_storeAccelCalibrationData(void)
+{
+    TDECL();
+    uint8_t buffer[3];
+
+    if (trySwitchState(SENSOR_STORE_CALIBRATION_DATA)) {
+        for (uint8_t i = 0; i < 3; i++)
+            buffer[i] = lsm6dsm_convertAccelOffsetValue(T(accelCalibrationData[i]));
+
+        SPI_MULTIWRITE(LSM6DSM_X_OFS_USR_ADDR, buffer, 3);
+
+        lsm6dsm_spiBatchTxRx(&T_SLAVE_INTERFACE(mode), lsm6dsm_spiCallback, NULL, __FUNCTION__);
+    } else
+        return false;
+
+    return true;
+}
+
+/*
+ * lsm6dsm_accelCfgData: set hw and sw accelerometer calibration values
+ * @data: calibration data struct.
+ * @cookie: private data.
+ */
+static bool lsm6dsm_accelCfgData(void *data, void *cookie)
+{
+    TDECL();
+    struct LSM6DSMAccelGyroCfgData *cfgData = data;
+
+#ifdef LSM6DSM_ACCEL_CALIB_ENABLED
+    accelCalBiasSet(&T(accelCal) , cfgData->sw[0], cfgData->sw[1], cfgData->sw[2]);
+#endif /* LSM6DSM_ACCEL_CALIB_ENABLED */
+
+    DEBUG_PRINT("Accel hw bias data [LSB]: %ld %ld %ld\n", cfgData->hw[0], cfgData->hw[1], cfgData->hw[2]);
+
+    memcpy(T(accelCalibrationData), cfgData->hw, 3 * sizeof(int32_t));
+
+    if (!lsm6dsm_storeAccelCalibrationData())
+        T(pendingStoreAccelCalibData) = true;
+
+    return true;
+}
+
+/*
+ * lsm6dsm_gyroCfgData: set hw and sw gyroscope calibration values
+ * @data: calibration data struct.
+ * @cookie: private data.
+ */
+static bool lsm6dsm_gyroCfgData(void *data, void *cookie)
+{
+    TDECL();
+    struct LSM6DSMAccelGyroCfgData *cfgData = data;
+
+#ifdef LSM6DSM_GYRO_CALIB_ENABLED
+    gyroCalSetBias(&T(gyroCal), cfgData->sw[0], cfgData->sw[1], cfgData->sw[2], sensorGetTime());
+#endif /* LSM6DSM_GYRO_CALIB_ENABLED */
+
+    DEBUG_PRINT("Gyro hw bias data [LSB]: %ld %ld %ld\n", cfgData->hw[0], cfgData->hw[1], cfgData->hw[2]);
+
+    memcpy(T(gyroCalibrationData), cfgData->hw, 3 * sizeof(int32_t));
+
+    return true;
+}
+
+/*
  * lsm6dsm_sensorInit: initial sensors configuration
  */
 static void lsm6dsm_sensorInit(void)
@@ -3711,6 +4084,11 @@
     }
 #endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
 
+    if (T(pendingStoreAccelCalibData)) {
+        T(pendingStoreAccelCalibData) = lsm6dsm_storeAccelCalibrationData();
+        return;
+    }
+
     if (T(pendingInt)) {
         T(pendingInt) = false;
         lsm6dsm_readStatusReg(false);
@@ -3848,6 +4226,10 @@
         break;
 
     case GYRO:
+        x -= (int16_t)T(gyroCalibrationData)[0];
+        y -= (int16_t)T(gyroCalibrationData)[1];
+        z -= (int16_t)T(gyroCalibrationData)[2];
+
         x_remap = LSM6DSM_REMAP_X_DATA(x, y, z, LSM6DSM_ACCEL_GYRO_ROT_MATRIX) * LSM6DSM_GYRO_KSCALE;
         y_remap = LSM6DSM_REMAP_Y_DATA(x, y, z, LSM6DSM_ACCEL_GYRO_ROT_MATRIX) * LSM6DSM_GYRO_KSCALE;
         z_remap = LSM6DSM_REMAP_Z_DATA(x, y, z, LSM6DSM_ACCEL_GYRO_ROT_MATRIX) * LSM6DSM_GYRO_KSCALE;
@@ -4249,6 +4631,20 @@
         returnIdle = true;
         break;
 
+    case SENSOR_CALIBRATION:
+        mSensor = (struct LSM6DSMSensor *)evtData;
+
+        if (T(calibrationState == CALIBRATION_COMPLETED)) {
+            returnIdle = true;
+        } else {
+            lsm6dsm_runCalibrationProgram(mSensor->idx);
+        }
+        break;
+
+    case SENSOR_STORE_CALIBRATION_DATA:
+        returnIdle = true;
+        break;
+
     case SENSOR_SELFTEST:
         mSensor = (struct LSM6DSMSensor *)evtData;
 
@@ -4528,6 +4924,10 @@
     case EVT_APP_FROM_HOST:
         break;
 
+    case EVT_SENSOR_RESTORE_IDLE:
+        lsm6dsm_processPendingEvt();
+        break;
+
     case EVT_TIME_SYNC:
         lsm6dsm_timeSyncTask();
         break;
@@ -4625,6 +5025,7 @@
     T(baroTimerId) = 0;
     T(pendingBaroTimerTask) = false;
 #endif /* LSM6DSM_I2C_MASTER_MAGNETOMETER_ENABLED, LSM6DSM_I2C_MASTER_BAROMETER_ENABLED */
+    memset(T(gyroCalibrationData), 0, 3 * sizeof(int32_t));
 
     slabSize = sizeof(struct TripleAxisDataEvent) + (LSM6DSM_MAX_NUM_COMMS_EVENT_SAMPLE * sizeof(struct TripleAxisDataPoint));