From 5e587a9224841d9c45b740d516f2432fe6215b56 Mon Sep 17 00:00:00 2001
From: Rahix <rahix@rahix.de>
Date: Thu, 22 Aug 2019 15:58:27 +0200
Subject: [PATCH] fix(bme680): Lock I2C bus when accessing it

Signed-off-by: Rahix <rahix@rahix.de>
---
 epicardium/modules/bme680.c | 161 +++++++++++++++++++++---------------
 1 file changed, 95 insertions(+), 66 deletions(-)

diff --git a/epicardium/modules/bme680.c b/epicardium/modules/bme680.c
index 293d46bf..d70f9938 100644
--- a/epicardium/modules/bme680.c
+++ b/epicardium/modules/bme680.c
@@ -20,6 +20,8 @@ static struct bme680_dev bme;
 static int convert_error(int8_t error)
 {
 	switch (error) {
+	case BME680_OK:
+		return 0;
 	case BME680_E_NULL_PTR:
 		return EFAULT;
 	case BME680_E_COM_FAIL:
@@ -37,74 +39,87 @@ int epic_bme680_init()
 {
 	int8_t result = BME680_OK;
 
-	if (__builtin_expect(!initialized, 0)) {
-		bme.dev_id   = BME680_I2C_ADDR_PRIMARY;
-		bme.intf     = BME680_I2C_INTF;
-		bme.read     = card10_bosch_i2c_read;
-		bme.write    = card10_bosch_i2c_write;
-		bme.delay_ms = card10_bosch_delay;
-
-		/*
-		 * amb_temp can be set to 25 prior to configuring the gas sensor
-		 * or by performing a few temperature readings without operating
-		 * the gas sensor.
-		 */
-		bme.amb_temp = 25;
-
-		result = bme680_init(&bme);
-		if (result != BME680_OK) {
-			LOG_ERR("bme680", "bme680_init error: %d\n", result);
-			return -convert_error(result);
-		}
-
-		/*
-		 * Select the power mode.  Must be set before writing the sensor
-		 * configuration
-		 */
-		bme.power_mode = BME680_FORCED_MODE;
-
-		/* Set the temperature, pressure and humidity settings */
-		bme.tph_sett.os_hum  = BME680_OS_2X;
-		bme.tph_sett.os_pres = BME680_OS_4X;
-		bme.tph_sett.os_temp = BME680_OS_8X;
-		bme.tph_sett.filter  = BME680_FILTER_SIZE_3;
-
-		/* Set the remaining gas sensor settings and link the heating profile */
-		bme.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
-		/* Create a ramp heat waveform in 3 steps */
-		bme.gas_sett.heatr_temp = HEATR_TEMP; /* degree Celsius */
-		bme.gas_sett.heatr_dur  = HEATR_DUR;  /* milliseconds */
-
-		/* Set the required sensor settings needed */
-		uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL |
-					BME680_OSH_SEL | BME680_FILTER_SEL |
-					BME680_GAS_SENSOR_SEL;
-
-		result = bme680_set_sensor_settings(settings_sel, &bme);
-		if (result != BME680_OK) {
-			LOG_ERR("bme680",
-				"bme680_set_sensor_settings error: %d\n",
-				result);
-			return -convert_error(result);
-		}
-
-		initialized = true;
+	if (initialized) {
+		return 0;
 	}
-	return 0;
+
+	if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
+		return -EBUSY;
+	}
+
+	bme.dev_id   = BME680_I2C_ADDR_PRIMARY;
+	bme.intf     = BME680_I2C_INTF;
+	bme.read     = card10_bosch_i2c_read;
+	bme.write    = card10_bosch_i2c_write;
+	bme.delay_ms = card10_bosch_delay;
+
+	/*
+	 * amb_temp can be set to 25 prior to configuring the gas sensor
+	 * or by performing a few temperature readings without operating
+	 * the gas sensor.
+	 */
+	bme.amb_temp = 25;
+
+	result = bme680_init(&bme);
+	if (result != BME680_OK) {
+		LOG_ERR("bme680", "bme680_init error: %d\n", result);
+		goto err;
+	}
+
+	/*
+	 * Select the power mode.  Must be set before writing the sensor
+	 * configuration
+	 */
+	bme.power_mode = BME680_FORCED_MODE;
+
+	/* Set the temperature, pressure and humidity settings */
+	bme.tph_sett.os_hum  = BME680_OS_2X;
+	bme.tph_sett.os_pres = BME680_OS_4X;
+	bme.tph_sett.os_temp = BME680_OS_8X;
+	bme.tph_sett.filter  = BME680_FILTER_SIZE_3;
+
+	/* Set the remaining gas sensor settings and link the heating profile */
+	bme.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
+	/* Create a ramp heat waveform in 3 steps */
+	bme.gas_sett.heatr_temp = HEATR_TEMP; /* degree Celsius */
+	bme.gas_sett.heatr_dur  = HEATR_DUR;  /* milliseconds */
+
+	/* Set the required sensor settings needed */
+	uint16_t settings_sel = BME680_OST_SEL | BME680_OSP_SEL |
+				BME680_OSH_SEL | BME680_FILTER_SEL |
+				BME680_GAS_SENSOR_SEL;
+
+	result = bme680_set_sensor_settings(settings_sel, &bme);
+	if (result != BME680_OK) {
+		LOG_ERR("bme680",
+			"bme680_set_sensor_settings error: %d\n",
+			result);
+		goto err;
+	}
+
+	initialized = true;
+	result      = BME680_OK;
+err:
+	hwlock_release(HWLOCK_I2C);
+	return -convert_error(result);
 }
 
 int epic_bme680_deinit()
 {
-	if (initialized) {
-		int8_t result = bme680_soft_reset(&bme);
-		if (result != BME680_OK) {
-			LOG_ERR("bme680",
-				"bme680_soft_reset error: %d\n",
-				result);
-			return -convert_error(result);
-		}
+	if (!initialized) {
+		return 0;
+	}
+
+	if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
+		return -EBUSY;
+	}
+
+	int8_t result = bme680_soft_reset(&bme);
+	if (result != BME680_OK) {
+		LOG_ERR("bme680", "bme680_soft_reset error: %d\n", result);
 	}
 
+	hwlock_release(HWLOCK_I2C);
 	initialized = false;
 	return 0;
 }
@@ -118,23 +133,34 @@ int epic_bme680_read_sensors(struct bme680_sensor_data *data)
 		return -EINVAL;
 	}
 
+	if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
+		return -EBUSY;
+	}
+
 	uint16_t profile_dur = 0;
 	bme680_get_profile_dur(&profile_dur, &bme);
 
 	result = bme680_set_sensor_mode(&bme); /* Trigger a measurement */
 	if (result != BME680_OK) {
 		LOG_ERR("bme680", "bme680_set_sensor_mode error: %d\n", result);
-		return -convert_error(result);
+		goto err;
 	}
 
-	vTaskDelay(pdMS_TO_TICKS(
-		profile_dur)); /* Wait for the measurement to complete */
+	/*
+	 * Wait for the measurement to complete.  Release the I2C lock in the
+	 * meantime.
+	 */
+	hwlock_release(HWLOCK_I2C);
+	vTaskDelay(pdMS_TO_TICKS(profile_dur));
+	if (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)) < 0) {
+		return -EBUSY;
+	}
 
 	struct bme680_field_data raw_data;
 	result = bme680_get_sensor_data(&raw_data, &bme);
 	if (result != BME680_OK) {
 		LOG_ERR("bme680", "bme680_get_sensor_data error: %d\n", result);
-		return -convert_error(result);
+		goto err;
 	}
 
 	data->temperature    = (float)raw_data.temperature / 100.0f;
@@ -142,5 +168,8 @@ int epic_bme680_read_sensors(struct bme680_sensor_data *data)
 	data->pressure       = raw_data.pressure / 100.0f;
 	data->gas_resistance = raw_data.gas_resistance;
 
-	return 0;
+	result = BME680_OK;
+err:
+	hwlock_release(HWLOCK_I2C);
+	return -convert_error(result);
 }
-- 
GitLab