mirror of
https://github.com/apache/nuttx.git
synced 2026-08-01 20:28:58 +00:00
Permissions (Part 2) Description: In kernel builds, any unprivileged process running on the NuttX device can open /dev/efuse and attempt to read/write fuse content. Reading the fuses may provide valuable information to an attacker controlling the user process. The write operation, in extreme cases where the fuse blocks are not locked, may brick the device. DISCLAIMER: I tried to be strict with the settings, better to relax them later if it's needed. This is part of https://github.com/apache/nuttx/issues/19410 See https://github.com/apache/nuttx/issues/19410 Compiles ok. Signed-off-by: Catalin Visinescu <catalin_visinescu@yahoo.com>
440 lines
12 KiB
C
440 lines
12 KiB
C
/****************************************************************************
|
|
* drivers/sensors/mlx90614.c
|
|
*
|
|
* 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.
|
|
*
|
|
****************************************************************************/
|
|
|
|
/* WARNING for developers:
|
|
*
|
|
* This driver uses the legacy style of writing sensor drivers for NuttX. The
|
|
* project has since decided to adopt a new sensor framework in order to
|
|
* have a consistent API and feature-set.
|
|
*
|
|
* Sensors which use the uORB framework are typically suffixed "_uorb". You
|
|
* can also visit the documentation about the new sensor framework to learn
|
|
* more.
|
|
*/
|
|
|
|
#warning "This is a deprecated legacy sensor driver."
|
|
|
|
/* Character driver for the Melexis MLX90614 Infrared Thermometer */
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <nuttx/debug.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <nuttx/fs/fs.h>
|
|
#include <nuttx/signal.h>
|
|
#include <nuttx/i2c/i2c_master.h>
|
|
#include <nuttx/sensors/ioctl.h>
|
|
#include <nuttx/sensors/mlx90614.h>
|
|
#include <nuttx/random.h>
|
|
|
|
#ifdef CONFIG_MLX90614_CRC
|
|
#include <nuttx/crc8.h>
|
|
#endif
|
|
|
|
#if defined(CONFIG_I2C) && defined(CONFIG_SENSORS_MLX90614)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct mlx90614_dev_s
|
|
{
|
|
FAR struct i2c_master_s *i2c; /* I2C interface */
|
|
uint8_t addr; /* I2C address */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* I2C Helpers */
|
|
|
|
static int mlx90614_read_word(FAR struct mlx90614_dev_s *priv,
|
|
uint8_t cmd, FAR uint16_t *regval);
|
|
static int mlx90614_write_word(FAR struct mlx90614_dev_s *priv,
|
|
uint8_t cmd, uint16_t regval);
|
|
|
|
/* Character driver methods */
|
|
|
|
static ssize_t mlx90614_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen);
|
|
static ssize_t mlx90614_write(FAR struct file *filep,
|
|
FAR const char *buffer, size_t buflen);
|
|
static int mlx90614_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_mlx90614_fops =
|
|
{
|
|
NULL, /* open */
|
|
NULL, /* close */
|
|
mlx90614_read, /* read */
|
|
mlx90614_write, /* write */
|
|
NULL, /* seek */
|
|
mlx90614_ioctl, /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: mlx90614_read_word
|
|
*
|
|
* Description:
|
|
* Read 16-bit register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int mlx90614_read_word(FAR struct mlx90614_dev_s *priv, uint8_t cmd,
|
|
FAR uint16_t *regval)
|
|
{
|
|
struct i2c_config_s config;
|
|
#ifdef CONFIG_MLX90614_CRC
|
|
uint8_t checkcrc[6];
|
|
uint8_t *buffer;
|
|
#else
|
|
uint8_t buffer[3];
|
|
#endif
|
|
uint8_t crc;
|
|
int ret;
|
|
|
|
#ifdef CONFIG_MLX90614_CRC
|
|
/* Recreate the I2C byte sequence to check the CRC PEC field */
|
|
|
|
checkcrc[0] = I2C_WRITEADDR8(priv->addr);
|
|
checkcrc[1] = cmd;
|
|
checkcrc[2] = I2C_READADDR8(priv->addr);
|
|
|
|
/* Point "buffer" to checkcrc[3] to fill it with received bytes */
|
|
|
|
buffer = (FAR uint8_t *)&checkcrc[3];
|
|
#endif
|
|
|
|
/* Set up the I2C configuration */
|
|
|
|
config.frequency = 100000;
|
|
config.address = priv->addr;
|
|
config.addrlen = 7;
|
|
|
|
/* Write the Command and read 16-bits + PEC from the device */
|
|
|
|
ret = i2c_writeread(priv->i2c, &config, (FAR const uint8_t *) &cmd, 1,
|
|
buffer, 3);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_writeread failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Copy the content of the buffer to the location of the uint16_t pointer */
|
|
|
|
*regval = (uint16_t)(buffer[0] | (buffer[1] << 8));
|
|
|
|
sninfo("value[0]: %02x | value[1]: %02x | value[2]: %02x | ret: %d\n",
|
|
buffer[0], buffer[1], buffer[2], ret);
|
|
|
|
#ifdef CONFIG_MLX90614_CRC
|
|
crc = crc8ccitt(checkcrc, 5);
|
|
sninfo("Calculated CRC: 0x%02X\n", crc);
|
|
|
|
if (crc != buffer[2])
|
|
{
|
|
snerr("Invalid CRC! Expected: 0x%02X read: 0x%02X\n", crc, buffer[2]);
|
|
return -EFAULT;
|
|
}
|
|
#endif
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mlx90614_write_word
|
|
*
|
|
* Description:
|
|
* Write to a 16-bit register
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int mlx90614_write_word(FAR struct mlx90614_dev_s *priv, uint8_t cmd,
|
|
uint16_t regval)
|
|
{
|
|
struct i2c_config_s config;
|
|
uint8_t pkgcrc[5];
|
|
uint8_t *buffer;
|
|
uint8_t crc;
|
|
int ret;
|
|
|
|
/* Set up the I2C configuration */
|
|
|
|
config.frequency = 100000;
|
|
config.address = priv->addr;
|
|
config.addrlen = 7;
|
|
|
|
/* First write 0x00 0x00 to erase the EEPROM */
|
|
|
|
pkgcrc[0] = I2C_WRITEADDR8(priv->addr);
|
|
pkgcrc[1] = cmd;
|
|
pkgcrc[2] = 0x00;
|
|
pkgcrc[3] = 0x00;
|
|
|
|
/* Generate the CRC to put in the PEC field */
|
|
|
|
crc = crc8ccitt(pkgcrc, 4);
|
|
sninfo("Generated CRC: 0x%02X\n", crc);
|
|
|
|
pkgcrc[4] = crc;
|
|
|
|
buffer = &(pkgcrc[1]);
|
|
|
|
/* Write the Command + 2 Bytes + PEC to device */
|
|
|
|
ret = i2c_write(priv->i2c, &config, (FAR const uint8_t *) buffer, 4);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_writeread failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Wait the EEPROM erase */
|
|
|
|
nxsched_usleep(10 * 1000);
|
|
|
|
/* Create the I2C command that will be sent to device */
|
|
|
|
pkgcrc[0] = I2C_WRITEADDR8(priv->addr);
|
|
pkgcrc[1] = cmd;
|
|
pkgcrc[2] = (regval & 0xff);
|
|
pkgcrc[3] = (regval & 0xff00) >> 8;
|
|
|
|
/* Generate the CRC to put in the PEC field */
|
|
|
|
crc = crc8ccitt(pkgcrc, 4);
|
|
sninfo("Generated CRC: 0x%02X\n", crc);
|
|
|
|
pkgcrc[4] = crc;
|
|
|
|
/* Write the Command + 2 Bytes + PEC to device */
|
|
|
|
ret = i2c_write(priv->i2c, &config, (FAR const uint8_t *) buffer, 4);
|
|
if (ret < 0)
|
|
{
|
|
snerr ("i2c_writeread failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
sninfo("New address 0x%02x stored correctly!\n", regval);
|
|
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mlx90614_read
|
|
****************************************************************************/
|
|
|
|
static ssize_t mlx90614_read(FAR struct file *filep, FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct inode *inode;
|
|
FAR struct mlx90614_dev_s *priv;
|
|
FAR struct mlx90614_temp_s *temp = (FAR struct mlx90614_temp_s *)buffer;
|
|
uint8_t cmd;
|
|
int ret;
|
|
|
|
inode = filep->f_inode;
|
|
|
|
DEBUGASSERT(inode->i_private);
|
|
priv = inode->i_private;
|
|
|
|
/* Check if the user is reading the right size */
|
|
|
|
if (buflen != sizeof(struct mlx90614_temp_s))
|
|
{
|
|
snerr("ERROR: You need to read 2 bytes from this sensor!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Read Ambient Temperature sensor */
|
|
|
|
cmd = MLX90614_CMD_RAM_ACCESS | MLX90614_TA;
|
|
|
|
ret = mlx90614_read_word(priv, cmd, &(temp->ta));
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Error reading thermometer sensor!\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Read Object1 Temperature sensor */
|
|
|
|
cmd = MLX90614_CMD_RAM_ACCESS | MLX90614_TOBJ1;
|
|
|
|
ret = mlx90614_read_word(priv, cmd, &(temp->tobj1));
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Error reading thermometer sensor!\n");
|
|
return ret;
|
|
}
|
|
|
|
/* Read Object2 Temperature sensor - some sensor doesn't have it */
|
|
|
|
cmd = MLX90614_CMD_RAM_ACCESS | MLX90614_TOBJ2;
|
|
|
|
ret = mlx90614_read_word(priv, cmd, &(temp->tobj2));
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Error reading thermometer sensor!\n");
|
|
return ret;
|
|
}
|
|
|
|
return buflen;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mlx90614_write
|
|
****************************************************************************/
|
|
|
|
static ssize_t mlx90614_write(FAR struct file *filep,
|
|
FAR const char *buffer, size_t buflen)
|
|
{
|
|
return -ENOSYS;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mlx90614_ioctl
|
|
****************************************************************************/
|
|
|
|
static int mlx90614_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct mlx90614_dev_s *priv = inode->i_private;
|
|
int ret = OK;
|
|
|
|
switch (cmd)
|
|
{
|
|
/* Change the device to use a new i2c/smbus address */
|
|
|
|
case SNIOC_CHANGE_SMBUSADDR:
|
|
{
|
|
FAR struct mlx90614_dev_s newdev;
|
|
FAR uint8_t *ptr = (FAR uint8_t *)((uintptr_t)arg);
|
|
uint16_t newaddr;
|
|
uint8_t smbcmd;
|
|
|
|
/* EEPROM reflash only happen when sent at I2C address 0x00 */
|
|
|
|
newdev.i2c = priv->i2c;
|
|
newdev.addr = 0x00;
|
|
|
|
/* The I2C address is put in the high byte position */
|
|
|
|
newaddr = (uint16_t) *ptr;
|
|
sninfo("Setting new address: 0x%02x\n", newaddr);
|
|
|
|
smbcmd = MLX90614_CMD_EEPROM_ACCESS | MLX90614_SMBUS_ADDR;
|
|
|
|
ret = mlx90614_write_word(&newdev, smbcmd, newaddr);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to change the I2C/SMBus address!\n");
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
snerr("ERROR: Unrecognized cmd: %d\n", cmd);
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: mlx90614_register
|
|
*
|
|
* Description:
|
|
* Register the MLX90614 character device as 'devpath'
|
|
*
|
|
* Input Parameters:
|
|
* devpath - The full path to the driver to register. E.g., "/dev/therm0"
|
|
* i2c - An instance of the I2C interface to use to communicate with
|
|
* MLX90614
|
|
* addr - The I2C address of the MLX90614.
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int mlx90614_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
|
|
uint8_t addr)
|
|
{
|
|
int ret;
|
|
|
|
/* Sanity check */
|
|
|
|
DEBUGASSERT(i2c != NULL);
|
|
|
|
/* Initialize the MLX90614 device structure */
|
|
|
|
FAR struct mlx90614_dev_s *priv =
|
|
kmm_malloc(sizeof(struct mlx90614_dev_s));
|
|
|
|
if (priv == NULL)
|
|
{
|
|
snerr("ERROR: Failed to allocate instance\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->i2c = i2c;
|
|
priv->addr = addr;
|
|
|
|
/* Register the character driver */
|
|
|
|
ret = register_driver(devpath, &g_mlx90614_fops, 0600, priv);
|
|
if (ret < 0)
|
|
{
|
|
snerr("ERROR: Failed to register driver: %d\n", ret);
|
|
kmm_free(priv);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* CONFIG_I2C && CONFIG_SENSORS_MLX90614 */
|