/**************************************************************************** * drivers/sensors/hts221.c * * Copyright (C) 2014 Haltian Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include /**************************************************************************** * Pre-Processor Definitions ****************************************************************************/ #ifdef CONFIG_HTS221_DEBUG # define hts221_dbg(x, ...) _info(x, ##__VA_ARGS__) #else # define hts221_dbg(x, ...) sninfo(x, ##__VA_ARGS__) #endif #ifndef CONFIG_HTS221_I2C_FREQUENCY # define CONFIG_HTS221_I2C_FREQUENCY 400000 #endif #define HTS221_WHO_AM_I 0x0f #define HTS221_AV_CONF 0x10 #define HTS221_CTRL_REG1 0x20 #define HTS221_CTRL_REG2 0x21 #define HTS221_CTRL_REG3 0x22 #define HTS221_STATUS_REG 0x27 #define HTS221_HUM_OUT_L 0x28 #define HTS221_HUM_OUT_H 0x29 #define HTS221_TEMP_OUT_L 0x2a #define HTS221_TEMP_OUT_H 0x2b /* Calibration registers */ #define HTS221_CALIB_H0_RH_X2 0x30 #define HTS221_CALIB_H1_RH_X2 0x31 #define HTS221_CALIB_T0_DEGC_X8 0x32 #define HTS221_CALIB_T1_DEGC_X8 0x33 #define HTS221_CALIB_T1_T0_MSB 0x35 #define HTS221_CALIB_H0T0_OUT_L 0x36 #define HTS221_CALIB_H0T0_OUT_H 0x37 #define HTS221_CALIB_H1T0_OUT_L 0x3a #define HTS221_CALIB_H1T0_OUT_H 0x3b #define HTS221_CALIB_T0_OUT_L 0x3c #define HTS221_CALIB_T0_OUT_H 0x3d #define HTS221_CALIB_T1_OUT_L 0x3e #define HTS221_CALIB_T1_OUT_H 0x3f /* HTS221_CTRL_REG1 */ #define HTS221_CTRL_REG1_PD (1 << 7) #define HTS221_CTRL_REG1_BDU (1 << 2) /* HTS221_CTRL_REG2 */ #define HTS221_CTRL_REG2_BOOT (1 << 7) #define HTS221_CTRL_REG2_ONE_SHOT (1 << 0) /* HTS221_CTRL_REG3 */ #define HTS221_CTRL_REG3_DRDY_L_H (1 << 7) #define HTS221_CTRL_REG3_PP_OD (1 << 6) #define HTS221_CTRL_REG3_DRDY_EN (1 << 2) /* HTS221_STATUS_REG */ #define HTS221_STATUS_REG_H_DA (1 << 1) #define HTS221_STATUS_REG_T_DA (1 << 0) #define HTS221_I2C_RETRIES 10 /**************************************************************************** * Private Function Prototypes *****************************************************************************/ static int hts221_open(FAR struct file *filep); static int hts221_close(FAR struct file *filep); static ssize_t hts221_read(FAR struct file *filep, FAR char *buffer, size_t buflen); static ssize_t hts221_write(FAR struct file *filep, FAR const char *buffer, size_t buflen); static int hts221_ioctl(FAR struct file *filep, int cmd, unsigned long arg); #ifndef CONFIG_DISABLE_POLL static int hts221_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup); #endif /**************************************************************************** * Private Types ****************************************************************************/ struct hts221_dev_s { struct i2c_master_s *i2c; uint8_t addr; hts221_config_t *config; sem_t devsem; volatile bool int_pending; #ifndef CONFIG_DISABLE_POLL struct pollfd *fds[CONFIG_HTS221_NPOLLWAITERS]; #endif struct { int16_t t0_out; int16_t t1_out; int16_t h0_t0_out; int16_t h1_t0_out; unsigned int t0_x8:10; unsigned int t1_x8:10; uint8_t h0_x2; uint8_t h1_x2; } calib; }; /**************************************************************************** * Private Data ****************************************************************************/ static const struct file_operations g_humidityops = { hts221_open, /* open */ hts221_close, /* close */ hts221_read, /* read */ hts221_write, /* write */ NULL, /* seek */ hts221_ioctl /* ioctl */ #ifndef CONFIG_DISABLE_POLL , hts221_poll /* poll */ #endif #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS , NULL /* unlink */ #endif }; /**************************************************************************** * Private Functions ****************************************************************************/ static int hts221_do_transfer(FAR struct hts221_dev_s *priv, FAR struct i2c_msg_s *msgv, size_t nmsg) { int ret = -EIO; int retries; for (retries = 0; retries < HTS221_I2C_RETRIES; retries++) { ret = I2C_TRANSFER(priv->i2c, msgv, nmsg); if (ret >= 0) { return 0; } else { /* Some error. Try to reset I2C bus and keep trying. */ #ifdef CONFIG_I2C_RESET if (retries == HTS221_I2C_RETRIES - 1) { break; } ret = I2C_RESET(priv->i2c); if (ret < 0) { hts221_dbg("I2C_RESET failed: %d\n", ret); return ret; } #endif } } hts221_dbg("xfer failed: %d\n", ret); return ret; } static int32_t hts221_write_reg8(FAR struct hts221_dev_s *priv, const uint8_t *command) { struct i2c_msg_s msgv[2] = { { .frequency = CONFIG_HTS221_I2C_FREQUENCY, .addr = priv->addr, .flags = 0, .buffer = (FAR void *)&command[0], .length = 1 }, { .frequency = CONFIG_HTS221_I2C_FREQUENCY, .addr = priv->addr, .flags = I2C_M_NOSTART, .buffer = (FAR void *)&command[1], .length = 1 } }; return hts221_do_transfer(priv, msgv, 2); } static int hts221_read_reg(FAR struct hts221_dev_s *priv, FAR const uint8_t *command, FAR uint8_t *value) { struct i2c_msg_s msgv[2] = { { .frequency = CONFIG_HTS221_I2C_FREQUENCY, .addr = priv->addr, .flags = 0, .buffer = (FAR void *)command, .length = 1 }, { .frequency = CONFIG_HTS221_I2C_FREQUENCY, .addr = priv->addr, .flags = I2C_M_READ, .buffer = value, .length = 1 } }; return hts221_do_transfer(priv, msgv, 2); } static int hts221_get_id(FAR struct hts221_dev_s *priv, uint8_t *value) { int ret = OK; uint8_t cmd = HTS221_WHO_AM_I; ret = hts221_read_reg(priv, &cmd, value); hts221_dbg("Who am I request: 0x%02X\n", *value); return ret; } static int hts221_cfgr_resolution(FAR struct hts221_dev_s *priv, FAR hts221_settings_t *settings) { int ret; uint8_t value; const uint8_t addr = HTS221_AV_CONF; uint8_t regval = 0; uint8_t cmd[2] = { 0 }; const uint8_t mask = 0x3f; ret = hts221_read_reg(priv, &addr, ®val); hts221_dbg("Default resolution: 0x%02X\n", regval); if (ret < 0) { return ERROR; } value = (uint8_t)settings->humid_resol | ((uint8_t)settings->temp_resol << 3); regval &= ~mask; cmd[0] = addr; cmd[1] = regval | value; hts221_dbg("New resolution: 0x%02X\n", cmd[1]); ret = hts221_write_reg8(priv, cmd); hts221_dbg("Resolution changed: temp=%d humid=%d ret=%d\n", settings->temp_resol, settings->humid_resol, ret); return ret; } static int hts221_config_ctrl_reg3(FAR struct hts221_dev_s *priv, FAR hts221_settings_t *settings) { int ret = OK; uint8_t regval = 0; uint8_t addr = HTS221_CTRL_REG3; const uint8_t mask = 0xc4; uint8_t data_to_write[2] = { 0 }; ret = hts221_read_reg(priv, &addr, ®val); hts221_dbg("CTRL_REG%d: 0x%02X\n", 3, regval); if (ret < 0) { return ERROR; } regval &= ~mask; regval |= (uint8_t)(settings->is_high_edge ? 0 : HTS221_CTRL_REG3_DRDY_L_H); regval |= (uint8_t)(settings->is_open_drain ? HTS221_CTRL_REG3_PP_OD : 0); regval |= (uint8_t)(settings->is_data_rdy ? HTS221_CTRL_REG3_DRDY_EN : 0); data_to_write[0] = addr; data_to_write[1] = regval; ret = hts221_write_reg8(priv, data_to_write); if (ret >= 0) { ret = hts221_read_reg(priv, &addr, ®val); if (ret >= 0) { hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n", data_to_write[1], 3, regval); } } return ret; } static int hts221_config_ctrl_reg2(FAR struct hts221_dev_s *priv, FAR hts221_settings_t *settings) { int ret = OK; uint8_t regval = 0; uint8_t addr = HTS221_CTRL_REG2; const uint8_t mask = 0x80; uint8_t data_to_write[2] = { 0 }; int retries = 5; if (!settings->is_boot) { return OK; } ret = hts221_read_reg(priv, &addr, ®val); hts221_dbg("CTRL_REG%d: 0x%02X\n", 2, regval); if (ret < 0) { return ERROR; } regval &= ~mask; regval |= HTS221_CTRL_REG2_BOOT; data_to_write[0] = addr; data_to_write[1] = regval; ret = hts221_write_reg8(priv, data_to_write); if (ret >= 0) { /* Wait until boot bit is cleared. */ do { ret = hts221_read_reg(priv, &addr, ®val); if (ret >= 0) { hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n", data_to_write[1], 2, regval); } else { break; } if ((regval & HTS221_CTRL_REG2_BOOT) == 0) { /* After boot bit is cleared, wait additional 5 msec as * recommended in HTS221 application note. */ up_mdelay(5); break; } usleep(10 * 1000); retries--; } while (retries); if (ret >= 0 && (regval & HTS221_CTRL_REG2_BOOT) != 0) { ret = -ETIMEDOUT; } } return ret; } static int hts221_config_ctrl_reg1(FAR struct hts221_dev_s *priv, FAR hts221_settings_t *settings) { int ret = OK; uint8_t regval = 0; uint8_t addr = HTS221_CTRL_REG1; const uint8_t mask = 0x87; uint8_t data_to_write[2] = { 0 }; ret = hts221_read_reg(priv, &addr, ®val); hts221_dbg("CTRL_REG%d: 0x%02X\n", 1, regval); if (ret < 0) { return ERROR; } regval &= ~mask; regval |= (uint8_t) (settings->odr & 0xFF); regval |= (uint8_t) (settings->is_bdu ? HTS221_CTRL_REG1_BDU : 0); data_to_write[0] = addr; data_to_write[1] = regval; ret = hts221_write_reg8(priv, data_to_write); if (ret >= 0) { ret = hts221_read_reg(priv, &addr, ®val); if (ret >= 0) { hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n", data_to_write[1], 1, regval); } } return ret; } static int hts221_power_on_off(FAR struct hts221_dev_s *priv, bool on) { int ret = OK; uint8_t regval = 0; uint8_t addr = HTS221_CTRL_REG1; uint8_t data_to_write[2]; ret = hts221_read_reg(priv, &addr, ®val); hts221_dbg("CTRL_REG%d: 0x%02X\n", 1, regval); if (ret < 0) { return ret; } if (on) { regval |= HTS221_CTRL_REG1_PD; } else { regval &= ~HTS221_CTRL_REG1_PD; } data_to_write[0] = addr; data_to_write[1] = regval; ret = hts221_write_reg8(priv, data_to_write); if (ret >= 0) { ret = hts221_read_reg(priv, &addr, ®val); if (ret >= 0) { hts221_dbg("wrote 0x%02X => CTRL_REG%d: 0x%02X\n", data_to_write[1], 1, regval); } } return ret; } static int hts221_config(FAR struct hts221_dev_s *priv, FAR hts221_settings_t *cfgr) { int ret = OK; ret = hts221_config_ctrl_reg2(priv, cfgr); /* Performs sensor reset. */ if (ret < 0) { return ERROR; } ret = hts221_cfgr_resolution(priv, cfgr); if (ret < 0) { return ERROR; } ret = hts221_config_ctrl_reg3(priv, cfgr); if (ret < 0) { return ERROR; } ret = hts221_config_ctrl_reg1(priv, cfgr); if (ret < 0) { return ERROR; } return ret; } static int hts221_start_conversion(FAR struct hts221_dev_s *priv) { int ret; uint8_t addr = HTS221_CTRL_REG2; uint8_t data_to_write[2]; ret = hts221_power_on_off(priv, true); if (ret < 0) { return ERROR; } data_to_write[0] = addr; data_to_write[1] = (uint8_t) HTS221_CTRL_REG2_ONE_SHOT; ret = hts221_write_reg8(priv, data_to_write); if (ret < 0) { hts221_dbg("Cannot start conversion\n"); ret = ERROR; } return ret; } static int hts221_check_status(FAR struct hts221_dev_s *priv, FAR hts221_status_t *status) { int ret = OK; uint8_t addr = HTS221_STATUS_REG; const uint8_t humid_mask = 0x02; const uint8_t temp_mask = 0x01; uint8_t regval = 0; ret = hts221_read_reg(priv, &addr, ®val); if (ret < 0) { return ERROR; } status->is_humid_ready = ((regval & humid_mask) ? true : false); status->is_temp_ready = ((regval & temp_mask) ? true : false); return ret; } static int hts221_read_raw_data(FAR struct hts221_dev_s *priv, FAR hts221_raw_data_t *data) { int ret = OK; uint8_t addr_humid_low = HTS221_HUM_OUT_L; uint8_t addr_humid_high = HTS221_HUM_OUT_H; uint8_t addr_temp_low = HTS221_TEMP_OUT_L; uint8_t addr_temp_high = HTS221_TEMP_OUT_H; irqstate_t flags; ret = hts221_read_reg(priv, &addr_humid_low, &data->humid_low_bits); if (ret < 0) { return ERROR; } ret = hts221_read_reg(priv, &addr_humid_high, &data->humid_high_bits); if (ret < 0) { return ERROR; } ret = hts221_read_reg(priv, &addr_temp_low, &data->temp_low_bits); if (ret < 0) { return ERROR; } ret = hts221_read_reg(priv, &addr_temp_high, &data->temp_high_bits); if (ret < 0) { return ERROR; } /* Add low-order bytes to entropy pool. */ add_sensor_randomness(((uint32_t)data->humid_low_bits << 8) | data->temp_low_bits); flags = enter_critical_section(); priv->int_pending = false; leave_critical_section(flags); hts221_dbg("Humid: 0x%02X, 0x%02X Temper: 0x%02X 0x%02X\n", data->humid_high_bits, data->humid_low_bits, data->temp_high_bits, data->temp_low_bits); return ret; } static int hts221_load_calibration_data(FAR struct hts221_dev_s *priv) { int ret; uint8_t addr; uint8_t t0_degc_x8 = 0; uint8_t t1_degc_x8 = 0; uint8_t t1_t0_msb = 0; uint8_t t0_out_lsb = 0; uint8_t t0_out_msb = 0; uint8_t t1_out_lsb = 0; uint8_t t1_out_msb = 0; uint8_t h0_rh_x2 = 0; uint8_t h1_rh_x2 = 0; uint8_t h0t0_out_lsb = 0; uint8_t h0t0_out_msb = 0; uint8_t h1t0_out_lsb = 0; uint8_t h1t0_out_msb = 0; addr = HTS221_CALIB_T0_DEGC_X8; ret = hts221_read_reg(priv, &addr, &t0_degc_x8); if (ret < 0) { return ret; } addr = HTS221_CALIB_T1_DEGC_X8; ret = hts221_read_reg(priv, &addr, &t1_degc_x8); if (ret < 0) { return ret; } addr = HTS221_CALIB_T1_T0_MSB; ret = hts221_read_reg(priv, &addr, &t1_t0_msb); if (ret < 0) { return ret; } addr = HTS221_CALIB_T0_OUT_L; ret = hts221_read_reg(priv, &addr, &t0_out_lsb); if (ret < 0) { return ret; } addr = HTS221_CALIB_T0_OUT_H; ret = hts221_read_reg(priv, &addr, &t0_out_msb); if (ret < 0) { return ret; } addr = HTS221_CALIB_T1_OUT_L; ret = hts221_read_reg(priv, &addr, &t1_out_lsb); if (ret < 0) { return ret; } addr = HTS221_CALIB_T1_OUT_H; ret = hts221_read_reg(priv, &addr, &t1_out_msb); if (ret < 0) { return ret; } addr = HTS221_CALIB_H0_RH_X2; ret = hts221_read_reg(priv, &addr, &h0_rh_x2); if (ret < 0) { return ret; } addr = HTS221_CALIB_H1_RH_X2; ret = hts221_read_reg(priv, &addr, &h1_rh_x2); if (ret < 0) { return ret; } addr = HTS221_CALIB_H0T0_OUT_L; ret = hts221_read_reg(priv, &addr, &h0t0_out_lsb); if (ret < 0) { return ret; } addr = HTS221_CALIB_H0T0_OUT_H; ret = hts221_read_reg(priv, &addr, &h0t0_out_msb); if (ret < 0) { return ret; } addr = HTS221_CALIB_H1T0_OUT_L; ret = hts221_read_reg(priv, &addr, &h1t0_out_lsb); if (ret < 0) { return ret; } addr = HTS221_CALIB_H1T0_OUT_H; ret = hts221_read_reg(priv, &addr, &h1t0_out_msb); if (ret < 0) { return ret; } priv->calib.t0_x8 = t0_degc_x8 | ((t1_t0_msb & 0x3) << 8); priv->calib.t1_x8 = t1_degc_x8 | ((t1_t0_msb & (0x3 << 2)) << (8 - 2)); priv->calib.t0_out = (uint16_t) (t0_out_lsb | (t0_out_msb << 8)); priv->calib.t1_out = (uint16_t) (t1_out_lsb | (t1_out_msb << 8)); priv->calib.h0_x2 = h0_rh_x2; priv->calib.h1_x2 = h1_rh_x2; priv->calib.h0_t0_out = (uint16_t) (h0t0_out_lsb | (h0t0_out_msb << 8)); priv->calib.h1_t0_out = (uint16_t) (h1t0_out_lsb | (h1t0_out_msb << 8)); hts221_dbg("calib.t0_x8: %d\n", priv->calib.t0_x8); hts221_dbg("calib.t1_x8: %d\n", priv->calib.t1_x8); hts221_dbg("calib.t0_out: %d\n", priv->calib.t0_out); hts221_dbg("calib.t1_out: %d\n", priv->calib.t1_out); hts221_dbg("calib.h0_x2: %d\n", priv->calib.h0_x2); hts221_dbg("calib.h1_x2: %d\n", priv->calib.h1_x2); hts221_dbg("calib.h0_t0_out: %d\n", priv->calib.h0_t0_out); hts221_dbg("calib.h1_t0_out: %d\n", priv->calib.h1_t0_out); /* As calibration coefficients are unique to each sensor device, * they are a good candidate to be added to entropy pool. */ up_rngaddentropy(RND_SRC_HW, (uint32_t *)&priv->calib, sizeof(priv->calib) / sizeof(uint32_t)); return OK; } static int hts221_calculate_temperature(FAR struct hts221_dev_s *priv, FAR int *temperature, FAR hts221_raw_data_t *raw_data) { int16_t t_out = (raw_data->temp_high_bits << 8) | raw_data->temp_low_bits; int x0 = priv->calib.t0_out; int x1 = priv->calib.t1_out; int y0 = priv->calib.t0_x8; int y1 = priv->calib.t1_x8; int x = t_out; int64_t y; int x1_x0_diff; x1_x0_diff = x1 - x0; y = (y0 * x1_x0_diff + (y1 - y0) * (x - x0)); y *= HTS221_TEMPERATURE_PRECISION; y /= x1_x0_diff * 8; *temperature = (int)y; hts221_dbg("Interpolation data temper: %d\n", *temperature); return OK; } static int hts221_calculate_humidity(FAR struct hts221_dev_s *priv, FAR unsigned int *humidity, FAR hts221_raw_data_t *raw_data) { int16_t h_out = (raw_data->humid_high_bits << 8) | raw_data->humid_low_bits; int x0 = priv->calib.h0_t0_out; int x1 = priv->calib.h1_t0_out; int y0 = priv->calib.h0_x2; int y1 = priv->calib.h1_x2; int x = h_out; int64_t y; int x1_x0_diff; x1_x0_diff = x1 - x0; y = (y0 * x1_x0_diff + (y1 - y0) * (x - x0)); y *= HTS221_HUMIDITY_PRECISION; y /= x1_x0_diff * 2; *humidity = (int)y; hts221_dbg("Interpolation data humidity: %d\n", *humidity); return OK; } static int hts221_read_convert_data(FAR struct hts221_dev_s *priv, FAR hts221_conv_data_t *data) { int ret = OK; hts221_raw_data_t raw_data; ret = hts221_read_raw_data(priv, &raw_data); if (ret < 0) { return ERROR; } ret = hts221_calculate_temperature(priv, &data->temperature, &raw_data); if (ret < 0) { return ERROR; } hts221_dbg("Temperature calculated\n"); ret = hts221_calculate_humidity(priv, &data->humidity, &raw_data); if (ret < 0) { return ERROR; } hts221_dbg("Humidity calculated\n"); return ret; } #ifdef CONFIG_HTS221_DEBUG static int hts221_dump_registers(FAR struct hts221_dev_s *priv) { int ret = OK; uint8_t av_addr = HTS221_AV_CONF; uint8_t ctrl_reg1_addr = HTS221_CTRL_REG1; uint8_t ctrl_reg2_addr = HTS221_CTRL_REG2; uint8_t ctrl_reg3_addr = HTS221_CTRL_REG3; uint8_t regval = 0; ret = hts221_read_reg(priv, &av_addr, ®val); if (ret < 0) { return ERROR; } hts221_dbg("AV_CONF_REG: 0x%02X\n", regval); ret = hts221_read_reg(priv, &ctrl_reg1_addr, ®val); if (ret < 0) { return ERROR; } hts221_dbg("CTRL_REG_1: 0x%02X\n", regval); ret = hts221_read_reg(priv, &ctrl_reg2_addr, ®val); if (ret < 0) { return ERROR; } hts221_dbg("CTRL_REG_2: 0x%02X\n", regval); ret = hts221_read_reg(priv, &ctrl_reg3_addr, ®val); if (ret < 0) { return ERROR; } hts221_dbg("CTRL_REG_3: 0x%02X\n", regval); return ret; } #endif static int hts221_open(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct hts221_dev_s *priv = inode->i_private; int ret; /* Get exclusive access */ do { ret = nxsem_wait(&priv->devsem); /* The only case that an error should occur here is if the wait was * awakened by a signal. */ DEBUGASSERT(ret == OK || ret == -EINTR); } while (ret == -EINTR); priv->config->set_power(priv->config, true); priv->config->irq_enable(priv->config, true); nxsem_post(&priv->devsem); hts221_dbg("Sensor is powered on\n"); return OK; } static int hts221_close(FAR struct file *filep) { FAR struct inode *inode = filep->f_inode; FAR struct hts221_dev_s *priv = inode->i_private; int ret; /* Get exclusive access */ do { ret = nxsem_wait(&priv->devsem); /* The only case that an error should occur here is if the wait was * awakened by a signal. */ DEBUGASSERT(ret == OK || ret == -EINTR); } while (ret == -EINTR); priv->config->irq_enable(priv->config, false); ret = hts221_power_on_off(priv, false); priv->config->set_power(priv->config, false); nxsem_post(&priv->devsem); hts221_dbg("CLOSED\n"); return ret; } static ssize_t hts221_read(FAR struct file *filep, FAR char *buffer, size_t buflen) { FAR struct inode *inode = filep->f_inode; FAR struct hts221_dev_s *priv = inode->i_private; hts221_conv_data_t data; ssize_t length = 0; int ret; /* Get exclusive access */ do { ret = nxsem_wait(&priv->devsem); /* The only case that an error should occur here is if the wait was * awakened by a signal. */ DEBUGASSERT(ret == OK || ret == -EINTR); } while (ret == -EINTR); ret = hts221_read_convert_data(priv, &data); if (ret < 0) { hts221_dbg("cannot read data: %d\n", ret); } else { /* This interface is mainly intended for easy debugging in nsh. */ length = snprintf(buffer, buflen, "%d %u\n", data.temperature, data.humidity); if (length > buflen) { length = buflen; } } nxsem_post(&priv->devsem); return length; } static ssize_t hts221_write(FAR struct file *filep, FAR const char *buffer, size_t buflen) { ssize_t length = 0; return length; } static int hts221_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; FAR struct hts221_dev_s *priv = inode->i_private; int ret; /* Get exclusive access */ do { ret = nxsem_wait(&priv->devsem); /* The only case that an error should occur here is if the wait was * awakened by a signal. */ DEBUGASSERT(ret == OK || ret == -EINTR); } while (ret == -EINTR); switch (cmd) { case SNIOC_GET_DEV_ID: ret = hts221_get_id(priv, (FAR uint8_t *) arg); break; case SNIOC_CFGR: ret = hts221_config(priv, (FAR hts221_settings_t *) arg); break; case SNIOC_START_CONVERSION: ret = hts221_start_conversion(priv); break; case SNIOC_CHECK_STATUS_REG: ret = hts221_check_status(priv, (FAR hts221_status_t *) arg); break; case SNIOC_READ_RAW_DATA: ret = hts221_read_raw_data(priv, (FAR hts221_raw_data_t *) arg); break; #ifdef CONFIG_HTS221_DEBUG case SNIOC_DUMP_REGS: ret = hts221_dump_registers(priv); break; #endif case SNIOC_READ_CONVERT_DATA: ret = hts221_read_convert_data(priv, (FAR hts221_conv_data_t *) arg); break; default: ret = -ENOTTY; break; } nxsem_post(&priv->devsem); return ret; } #ifndef CONFIG_DISABLE_POLL static bool hts221_sample(FAR struct hts221_dev_s *priv) { int ret; hts221_status_t status = { .is_humid_ready = false, .is_temp_ready = false }; ret = hts221_check_status(priv, &status); if (ret < 0) { return false; } return status.is_humid_ready || status.is_temp_ready; } static void hts221_notify(FAR struct hts221_dev_s *priv) { DEBUGASSERT(priv != NULL); int i; /* If there are threads waiting on poll() for data to become available, * then wake them up now. NOTE: we wake up all waiting threads because we * do not know that they are going to do. If they all try to read the data, * then some make end up blocking after all. */ for (i = 0; i < CONFIG_HTS221_NPOLLWAITERS; i++) { FAR struct pollfd *fds = priv->fds[i]; if (fds) { fds->revents |= POLLIN; hts221_dbg("Report events: %02x\n", fds->revents); nxsem_post(fds->sem); } } } static int hts221_poll(FAR struct file *filep, FAR struct pollfd *fds, bool setup) { FAR struct inode *inode; FAR struct hts221_dev_s *priv; irqstate_t flags; int ret; int i; DEBUGASSERT(filep && fds); inode = filep->f_inode; DEBUGASSERT(inode && inode->i_private); priv = (FAR struct hts221_dev_s *)inode->i_private; /* Get exclusive access */ do { ret = nxsem_wait(&priv->devsem); /* The only case that an error should occur here is if the wait was * awakened by a signal. */ DEBUGASSERT(ret == OK || ret == -EINTR); } while (ret == -EINTR); if (setup) { /* Ignore waits that do not include POLLIN */ if ((fds->events & POLLIN) == 0) { ret = -EDEADLK; goto out; } /* This is a request to set up the poll. Find an available slot for * the poll structure reference. */ for (i = 0; i < CONFIG_HTS221_NPOLLWAITERS; i++) { /* Find an available slot */ if (!priv->fds[i]) { /* Bind the poll structure and this slot */ priv->fds[i] = fds; fds->priv = &priv->fds[i]; break; } } if (i >= CONFIG_HTS221_NPOLLWAITERS) { fds->priv = NULL; ret = -EBUSY; goto out; } flags = enter_critical_section(); if (priv->int_pending || hts221_sample(priv)) { hts221_notify(priv); } leave_critical_section(flags); } else if (fds->priv) { /* This is a request to tear down the poll. */ struct pollfd **slot = (struct pollfd **)fds->priv; DEBUGASSERT(slot != NULL); /* Remove all memory of the poll setup */ *slot = NULL; fds->priv = NULL; } out: nxsem_post(&priv->devsem); return ret; } #endif /* !CONFIG_DISABLE_POLL */ static int hts221_int_handler(int irq, FAR void *context, FAR void *arg) { FAR struct hts221_dev_s *priv = (FAR struct hts221_dev_s *)arg; DEBUGASSERT(priv != NULL); priv->int_pending = true; hts221_dbg("Hts221 interrupt\n"); #ifndef CONFIG_DISABLE_POLL hts221_notify(priv); #endif return OK; } int hts221_register(FAR const char *devpath, FAR struct i2c_master_s *i2c, uint8_t addr, FAR hts221_config_t *config) { int ret = 0; FAR struct hts221_dev_s *priv; priv = (struct hts221_dev_s *)kmm_zalloc(sizeof(struct hts221_dev_s)); if (!priv) { hts221_dbg("Memory cannot be allocated for hts221 sensor"); return -ENOMEM; } priv->addr = addr; priv->i2c = i2c; priv->config = config; nxsem_init(&priv->devsem, 0, 1); priv->config->set_power(priv->config, true); ret = hts221_load_calibration_data(priv); if (ret < 0) { kmm_free(priv); hts221_dbg("Cannot calibrate hts221 sensor\n"); return -EAGAIN; } ret = register_driver(devpath, &g_humidityops, 0666, priv); hts221_dbg("Registered with %d\n", ret); if (ret < 0) { kmm_free(priv); hts221_dbg("Error occurred during the driver registering\n"); return ret; } if (priv->config->irq_clear) { priv->config->irq_clear(priv->config); } priv->config->irq_attach(priv->config, hts221_int_handler, priv); priv->config->irq_enable(priv->config, false); priv->config->set_power(priv->config, false); return OK; }