Donny9 commented on code in PR #15525: URL: https://github.com/apache/nuttx/pull/15525#discussion_r1914024436
########## drivers/sensors/mcp9600.c: ########## @@ -0,0 +1,861 @@ +/**************************************************************************** + * drivers/sensors/mcp9600.c + * + * Contributed by Matteo Golin + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <assert.h> +#include <debug.h> +#include <errno.h> + +#include <nuttx/fs/fs.h> +#include <nuttx/i2c/i2c_master.h> +#include <nuttx/kmalloc.h> +#include <nuttx/sensors/mcp9600.h> +#include <stdio.h> + +#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_MCP9600) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* I2C frequency to use during transfers */ + +#ifdef CONFIG_MCP9600_I2C_FREQUENCY +#define CONFIG_MCP9600_I2C_FREQUENCY 100000 +#endif + +#define REG_THERMO_HOT_JUNC 0x0 /* Thermocouple Hot-Junction, T H */ +#define REG_JUNC_TEMP_DELTA 0x1 /* Junctions Temperature Delta, TΔ */ +#define REG_COLD_JUNC_TEMP 0x2 /* Cold-Junction Temperature, T C */ +#define REG_RAW_ADC 0x3 /* Raw ADC Data */ +#define REG_STATUS 0x4 /* STATUS */ +#define REG_THERMO_SEN_CONF 0x5 /* Thermocouple Sensor Configuration */ +#define REG_DEV_CONFIG 0x6 /* Device Configuration */ +#define REG_ALERT1_CONF 0x8 /* Alert 1 Configuration */ +#define REG_ALERT2_CONF 0x9 /* Alert 2 Configuration */ +#define REG_ALERT3_CONF 0xa /* Alert 3 Configuration */ +#define REG_ALERT4_CONF 0xb /* Alert 4 Configuration */ +#define REG_ALERT1_HYST 0xc /* Alert 1 Hysteresis, THYST1 */ +#define REG_ALERT2_HYST 0xd /* Alert 2 Hysteresis, THYST2 */ +#define REG_ALERT3_HYST 0xe /* Alert 3 Hysteresis, THYST3 */ +#define REG_ALERT4_HYST 0xf /* Alert 4 Hysteresis, THYST4 */ +#define REG_ALERT1_TEMP 0x10 /* Temperature Alert 1 Limit, TALERT1 */ +#define REG_ALERT2_TEMP 0x11 /* Temperature Alert 2 Limit, TALERT2 */ +#define REG_ALERT3_TEMP 0x12 /* Temperature Alert 3 Limit, TALERT3 */ +#define REG_ALERT4_TEMP 0x13 /* Temperature Alert 4 Limit, TALERT4 */ +#define REG_DEVID 0x20 /* Device ID/Revision */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mcp9600_dev_s +{ + FAR struct i2c_master_s *i2c; /* I2C interface */ + uint8_t addr; /* I2C address */ + struct mcp9600_devconf_s conf; /* Device configuration */ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + int16_t crefs; /* Number of open references. */ + bool unlinked; /* True, driver has been unlinked. */ +#endif + mutex_t devlock; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int mcp9600_open(FAR struct file *filep); +static int mcp9600_close(FAR struct file *filep); +static ssize_t mcp9600_write(FAR struct file *filep, FAR const char *buffer, + size_t buflen); +static ssize_t mcp9600_read(FAR struct file *filep, FAR char *buffer, + size_t buflen); +static int mcp9600_ioctl(FAR struct file *filep, int cmd, unsigned long arg); +static int mcp9600_unlink(FAR struct inode *inode); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct file_operations g_mcp9600fops = +{ +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + .open = mcp9600_open, + .close = mcp9600_close, +#else + .open = NULL, + .close = NULL, +#endif + .read = mcp9600_read, + .write = mcp9600_write, + .seek = NULL, + .ioctl = mcp9600_ioctl, + .mmap = NULL, + .truncate = NULL, + .poll = NULL, +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + .unlink = mcp9600_unlink, +#endif +}; + +/* Alert hysterisis registers */ + +static const enum mcp9600_alert_e ALERT_HYSTS[] = +{ + [MCP9600_ALERT1] = REG_ALERT1_HYST, + [MCP9600_ALERT2] = REG_ALERT2_HYST, + [MCP9600_ALERT3] = REG_ALERT3_HYST, + [MCP9600_ALERT4] = REG_ALERT4_HYST, +}; + +/* Alert limit registers */ + +static const enum mcp9600_alert_e ALERT_LIMITS[] = +{ + [MCP9600_ALERT1] = REG_ALERT1_TEMP, + [MCP9600_ALERT2] = REG_ALERT2_TEMP, + [MCP9600_ALERT3] = REG_ALERT3_TEMP, + [MCP9600_ALERT4] = REG_ALERT4_TEMP, +}; + +/* Alert configuration registers */ + +static const enum mcp9600_alert_e ALERT_CONFIGS[] = +{ + [MCP9600_ALERT1] = REG_ALERT1_CONF, + [MCP9600_ALERT2] = REG_ALERT2_CONF, + [MCP9600_ALERT3] = REG_ALERT3_CONF, + [MCP9600_ALERT4] = REG_ALERT4_CONF, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mcp9600_read_reg + * + * Description: + * Reads the value of a single register into the buffer. Since registers + * are variable in size, the number of bytes to read can be specified. + * + ****************************************************************************/ + +static int mcp9600_read_reg(FAR struct mcp9600_dev_s *priv, uint8_t reg, + void *buf, size_t nbytes) +{ + struct i2c_msg_s read_cmd[] = { + { + .frequency = CONFIG_MCP9600_I2C_FREQUENCY, + .addr = priv->addr, + .flags = 0, + .buffer = ®, + .length = sizeof(reg), + }, + { + .frequency = CONFIG_MCP9600_I2C_FREQUENCY, + .addr = priv->addr, + .flags = I2C_M_READ, + .buffer = buf, + .length = nbytes, + }, + }; + + return I2C_TRANSFER(priv->i2c, read_cmd, + sizeof(read_cmd) / sizeof(struct i2c_msg_s)); +} + +/**************************************************************************** + * Name: mcp9600_write_reg + * + * Description: + * Writes `nbytes` of the value in `buf` to the register specified by + * `reg`. + * + ****************************************************************************/ + +static int mcp9600_write_reg(FAR struct mcp9600_dev_s *priv, uint8_t reg, + void *buf, size_t nbytes) +{ + struct i2c_msg_s read_cmd[] = { + { + .frequency = CONFIG_MCP9600_I2C_FREQUENCY, + .addr = priv->addr, + .flags = 0, + .buffer = ®, + .length = sizeof(reg), + }, + { + .frequency = CONFIG_MCP9600_I2C_FREQUENCY, + .addr = priv->addr, + .flags = 0, + .buffer = buf, + .length = nbytes, + }, + }; + + return I2C_TRANSFER(priv->i2c, read_cmd, + sizeof(read_cmd) / sizeof(struct i2c_msg_s)); +} + +/**************************************************************************** + * Name: mcp9600_read_temp + * + * Description: + * Reads the value of a temperature register and performs the conversion to + * put it into degrees Celsius. + * + ****************************************************************************/ + +static int mcp9600_read_temp(FAR struct mcp9600_dev_s *priv, uint8_t reg, + int16_t *temp) +{ + int err; + uint8_t raw[2]; + + err = mcp9600_read_reg(priv, reg, raw, sizeof(raw)); + if (err < 0) + { + return err; + } + + /* Positive temperature */ + + *temp = (raw[0] * 16 + raw[1] / 16); + + /* Negative temperature */ + + if (raw[0] & 0x80) + { + *temp -= 4096; + } + + return err; +} + +/**************************************************************************** + * Name: mcp9600_config_alert + * + * Description: + * Configure an alert of the MCP9600. + * + ****************************************************************************/ + +static int mcp9600_config_alert(FAR struct mcp9600_dev_s *priv, + struct mcp9600_alert_conf_s *config) +{ + int err; + + /* Configure hysteresis threshold first */ + + err = mcp9600_write_reg(priv, ALERT_HYSTS[config->alert], &config->temp, + sizeof(config->temp)); + if (err < 0) + { + return err; + } + + /* Configure limit */ + + int16_t limit = config->limit << 2; /* 2 LSBs must be 0 for this reg */ + err = mcp9600_write_reg(priv, ALERT_LIMITS[config->alert], &limit, + sizeof(limit)); + if (err < 0) + { + return err; + } + + /* Configure the config register */ + + uint8_t config_reg = 0; + config_reg |= (config->enable); + config_reg |= (config->int_mode << 1); + config_reg |= (config->active_high << 2); + config_reg |= (config->falling_temp << 3); + config_reg |= (config->cold_junc << 4); + + return mcp9600_write_reg(priv, ALERT_CONFIGS[config->alert], &config_reg, + sizeof(config_reg)); +} + +/**************************************************************************** + * Name: mcp9600_validate_conf + * + * Description: + * Validates the device configuration settings passed by a user. Returns + * -EINVAL if any field is invalid, and returns 0 if okay. + * + ****************************************************************************/ + +static int mcp9600_validate_conf(struct mcp9600_devconf_s *conf) +{ + if (conf == NULL) + { + return -EINVAL; + } + + if (conf->thermo_type < MCP9600_THERMO_TYPE_K || + conf->thermo_type > MCP9600_THERMO_TYPE_R) + { + return -EINVAL; + } + + if (conf->filter_coeff > 0 || conf->filter_coeff > 8) + { + return -EINVAL; + } + + if (conf->resolution < MCP9600_ADC_RES_18 || + conf->resolution > MCP9600_ADC_RES_12) + { + return -EINVAL; + } + + if (conf->num_samples < MCP9600_SAMPLE_1 || + conf->num_samples > MCP9600_SAMPLE_128) + { + return -EINVAL; + } + + if (conf->mode < MCP9600_MODE_NORMAL || conf->mode > MCP9600_MODE_BURST) + { + return -EINVAL; + } + + if (conf->cold_res != MCP9600_COLDRES_0625 || + conf->cold_res != MCP9600_COLDRES_25) + { + return -EINVAL; + } + + return 0; +} + +/**************************************************************************** + * Name: mcp9600_open + * + * Description: + * This function is called whenever the MCP9600 device is opened. + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int mcp9600_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct mcp9600_dev_s *priv = inode->i_private; + int err; + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + /* Increment the count of open references on the driver */ + + priv->crefs++; + DEBUGASSERT(priv->crefs > 0); + + nxmutex_unlock(&priv->devlock); + return 0; +} +#endif + +/**************************************************************************** + * Name: mcp9600_close + * + * Description: + * This routine is called when the MCP9600 device is closed. + * + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int mcp9600_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct mcp9600_dev_s *priv = inode->i_private; + int err; + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + /* Decrement the count of open references on the driver */ + + DEBUGASSERT(priv->crefs > 0); + priv->crefs--; + + /* If the count has decremented to zero and the driver has been unlinked, + * then free memory now. + */ + + if (priv->crefs <= 0 && priv->unlinked) + { + nxmutex_destroy(&priv->devlock); + kmm_free(priv); + return 0; + } + + nxmutex_unlock(&priv->devlock); + return 0; +} +#endif + +/**************************************************************************** + * Name: mcp9600_unlink + ****************************************************************************/ + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS +static int mcp9600_unlink(FAR struct inode *inode) +{ + FAR struct mcp9600_dev_s *priv; + int err; + + DEBUGASSERT(inode->i_private != NULL); + priv = inode->i_private; + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + + /* Are there open references to the driver data structure? */ + + if (priv->crefs <= 0) + { + nxmutex_destroy(&priv->devlock); + kmm_free(priv); + return 0; + } + + /* No. Just mark the driver as unlinked and free the resources when + * the last client closes their reference to the driver. + */ + + priv->unlinked = true; + nxmutex_unlock(&priv->devlock); + return OK; +} +#endif + +/**************************************************************************** + * Name: mcp9600_read + * + * Description: + * Character driver interface to sensor for debugging. + * + ****************************************************************************/ + +static ssize_t mcp9600_read(FAR struct file *filep, FAR char *buffer, + size_t buflen) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct mcp9600_dev_s *priv = inode->i_private; + int length = 0; + int err; + + /* If file position is non-zero, then we're at the end of file. */ + + if (filep->f_pos > 0) + { + return 0; + } + + /* Get exclusive access */ + + err = nxmutex_lock(&priv->devlock); + if (err < 0) + { + return err; + } + +#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS + if (priv->unlinked) + { + /* Do not allow operations on unlinked sensors. This allows + * sensor use on hot swappable I2C bus. + */ + + nxmutex_unlock(&priv->devlock); + return 0; + } +#endif + + int16_t hot_junc_temp; Review Comment: move to after line 484 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
