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>
635 lines
19 KiB
C
635 lines
19 KiB
C
/****************************************************************************
|
|
* drivers/sensors/amg88xx.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."
|
|
|
|
/****************************************************************************
|
|
* Included Files
|
|
****************************************************************************/
|
|
|
|
#include <nuttx/config.h>
|
|
#include <nuttx/mutex.h>
|
|
#include <nuttx/sensors/amg88xx.h>
|
|
|
|
#include <errno.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <nuttx/debug.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 <unistd.h>
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/* Register bits definitions */
|
|
|
|
/* Power Control Register */
|
|
|
|
#define AMG88XX_POWER_CONTROL_REGISTER (0x00)
|
|
#define AMG88XX_NORMAL_MODE_CMD (0x00)
|
|
#define AMG88XX_SLEEP_MODE_CMD (0x10)
|
|
|
|
/* Reset Register */
|
|
|
|
#define AMG88XX_RESET_REGISTER (0x01)
|
|
#define AMG88XX_FLAG_RESET_CMD (0x30)
|
|
#define AMG88XX_INITIAL_RESET_CMD (0x30)
|
|
|
|
/* Frame Rate Register */
|
|
|
|
#define AMG88XX_FRAME_RATE_REGISTER (0x02)
|
|
#define AMG88XX_FPS_BIT (0x00)
|
|
#define AMG88XX_1FPS (0x01)
|
|
#define AMG88XX_10FPS (0x00)
|
|
|
|
/* Interrupt Control Register */
|
|
|
|
#define AMG88XX_INTERRUPT_CONTROL_REGISTER (0x03)
|
|
#define AMG88XX_INTMOD_BIT (0x01)
|
|
#define AMG88XX_INTMOD_ABS_MODE (0x01)
|
|
#define AMG88XX_INTMOD_DIFF_MODE (0x01)
|
|
#define AMG88XX_INTEN_BIT (0x01)
|
|
#define AMG88XX_INTEN_ACTIVE (0x01)
|
|
#define AMG88XX_INTEN_INATIVE (0x00)
|
|
|
|
/* Status Register */
|
|
|
|
#define AMG88XX_STATUS_REGISTER (0x04)
|
|
#define AMG88XX_OVF_IRS (0x02)
|
|
#define AMG88XX_INTF (0x01)
|
|
|
|
/* Status Clear Register */
|
|
|
|
#define AMG88XX_STATUS_CLEAR_REGISTER (0x05)
|
|
#define AMG88XX_OVF_CLR (0x02)
|
|
#define AMG88XX_INT_CLR (0x01)
|
|
|
|
/* Average Register */
|
|
|
|
#define AMG88XX_AVERAGE_REGISTER (0x07)
|
|
#define AMG88XX_MAMOD_BIT (0x05)
|
|
#define AMG88XX_MAMOD_ENABLE (0x01)
|
|
#define AMG88XX_MAMOD_DISABLE (0x00)
|
|
|
|
/* Setting average mode
|
|
*
|
|
* There is an difference between
|
|
* average register and average mode register
|
|
* Check the datasheet for clarification
|
|
*/
|
|
|
|
#define AMG88XX_AVERAGE_MODE_REGISTER (0x1F)
|
|
#define AMG88XX_AVEAGE_MODE_CMD_1 (0x50)
|
|
#define AMG88XX_AVEAGE_MODE_CMD_2 (0x45)
|
|
#define AMG88XX_AVEAGE_MODE_CMD_3 (0x57)
|
|
#define AMG88XX_AVEAGE_MODE_CMD_4 (0x00)
|
|
|
|
/* Interrupt Level Register */
|
|
|
|
#define AMG88XX_INT_HIGH_L (0x08)
|
|
#define AMG88XX_INT_HIGH_H (0x09)
|
|
#define AMG88XX_INT_LOW_L (0x0A)
|
|
#define AMG88XX_INT_LOW_H (0x0B)
|
|
#define AMG88XX_INT_HYST_L (0x0C)
|
|
#define AMG88XX_INT_HYST_H (0x0D)
|
|
#define AMG88XX_INT_MASK_H (0x0F)
|
|
|
|
/* Thermistor Register */
|
|
|
|
#define AMG88XX_THERMISTOR_L (0x0E)
|
|
#define AMG88XX_THERMISTOR_H (0x0F)
|
|
|
|
/* Interrupt Table Register */
|
|
|
|
#define AMG88XX_INTERRUPT_TABLE_REGISTER (0x10)
|
|
#define AMG88XX_INTERRUPT_TABLE_LENGTH (0x08)
|
|
|
|
/* Temperature Register */
|
|
|
|
#define AMG88XX_PIXEL_TABLE_BASE (0x80)
|
|
#define AMG88XX_PIXEL_TABLE_WIDTH (0x7F)
|
|
#define AMG88XX_PIXEL_WIDTH (0x02)
|
|
#define AMG88XX_PIXEL_MASK_H (0x0F)
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
struct amg88xx_dev_s
|
|
{
|
|
FAR struct i2c_master_s *i2c; /* I2C interface */
|
|
FAR struct amg88xx_config_s *config; /* sensor config structure */
|
|
mutex_t devlock;
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
static inline int amg88xx_i2c_write_addr(FAR struct amg88xx_dev_s *priv,
|
|
uint8_t regaddr,
|
|
uint8_t regval);
|
|
|
|
static inline int amg88xx_i2c_read_addr(FAR struct amg88xx_dev_s *priv,
|
|
uint8_t regaddr,
|
|
uint8_t *regval);
|
|
|
|
/* Character driver methods */
|
|
|
|
static int amg88xx_open(FAR struct file *filep);
|
|
|
|
static int amg88xx_close(FAR struct file *filep);
|
|
|
|
static ssize_t amg88xx_read(FAR struct file *filep,
|
|
FAR char *buffer,
|
|
size_t buflen);
|
|
|
|
static int amg88xx_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
static const struct file_operations g_amg88xx_fops =
|
|
{
|
|
amg88xx_open, /* open */
|
|
amg88xx_close, /* close */
|
|
amg88xx_read, /* read */
|
|
NULL, /* write */
|
|
NULL, /* seek */
|
|
amg88xx_ioctl /* ioctl */
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: amg88xx_i2c_write_addr
|
|
*
|
|
* Description:
|
|
* Write to an amg88xx register transaction pattern:
|
|
* Write COMMAND at REGISTER
|
|
* Sensor Address / W - Register Address - Command
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int amg88xx_i2c_write_addr(FAR struct amg88xx_dev_s *priv,
|
|
uint8_t regaddr,
|
|
uint8_t regval)
|
|
{
|
|
/* Write to an amg88xx register transaction pattern:
|
|
* Sensor Address - Register Address - Command
|
|
*/
|
|
|
|
int ret;
|
|
struct i2c_msg_s msg;
|
|
uint8_t data[2];
|
|
|
|
data[0] = regaddr;
|
|
data[1] = regval;
|
|
|
|
msg.addr = priv->config->addr;
|
|
msg.frequency = priv->config->speed;
|
|
msg.flags = I2C_M_NOSTOP;
|
|
msg.buffer = data;
|
|
msg.length = sizeof(data);
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, &msg, 1);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: amg88xx_i2c_read_addr
|
|
*
|
|
* Description:
|
|
* Read to an amg88xx register transaction pattern:
|
|
* Read COMMAND from REGISTER
|
|
* Sensor Address / W - Register Address - Sensor Address / R - Command
|
|
*
|
|
****************************************************************************/
|
|
|
|
static inline int amg88xx_i2c_read_addr(FAR struct amg88xx_dev_s *priv,
|
|
uint8_t regaddr,
|
|
FAR uint8_t *regval)
|
|
{
|
|
int ret;
|
|
struct i2c_msg_s msg[2];
|
|
|
|
msg[0].addr = priv->config->addr;
|
|
msg[0].frequency = priv->config->speed;
|
|
msg[0].flags = I2C_M_NOSTOP;
|
|
msg[0].buffer = ®addr;
|
|
msg[0].length = sizeof(regaddr);
|
|
|
|
msg[1].addr = priv->config->addr;
|
|
msg[1].frequency = priv->config->speed;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = regval;
|
|
msg[1].length = sizeof(*regval);
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: amg88xx_open
|
|
*
|
|
* Description:
|
|
* Character device open call.
|
|
* Opening the device will not ensure that data can be read immediately.
|
|
* Some time need to pass before data is available in the data registers.
|
|
* By default the device start with 10 FPS, but still, without not delay
|
|
* between open and read the data will be full 0x00.
|
|
*
|
|
* Input Parameters:
|
|
* filep - Pointer to struct file
|
|
*
|
|
* Returned Value:
|
|
* OK - Sensor device was opened successfully.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int amg88xx_open(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct amg88xx_dev_s *priv = inode->i_private;
|
|
uint8_t sleep;
|
|
int ret;
|
|
|
|
ret = nxmutex_lock(&priv->devlock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
amg88xx_i2c_read_addr(priv,
|
|
(uint8_t)AMG88XX_POWER_CONTROL_REGISTER,
|
|
(uint8_t *)&sleep);
|
|
|
|
/* Set the sensor to normal operation mode */
|
|
|
|
ret = amg88xx_i2c_write_addr(priv,
|
|
(uint8_t)AMG88XX_POWER_CONTROL_REGISTER,
|
|
(uint8_t)AMG88XX_NORMAL_MODE_CMD);
|
|
if (ret < 0)
|
|
{
|
|
nxmutex_unlock(&priv->devlock);
|
|
return ret;
|
|
}
|
|
|
|
if (sleep == AMG88XX_SLEEP_MODE_CMD)
|
|
{
|
|
/* Hardcoded value specified in the datasheet */
|
|
|
|
usleep(50000);
|
|
|
|
/* If sensor was in sleep mode, we write initial reset register
|
|
* same things as flag reset and read nonvolatile memory
|
|
* which store adjustment value of sensor.
|
|
*/
|
|
|
|
ret = amg88xx_i2c_write_addr(priv,
|
|
(uint8_t)AMG88XX_RESET_REGISTER,
|
|
(uint8_t)AMG88XX_INITIAL_RESET_CMD);
|
|
if (ret < 0)
|
|
{
|
|
nxmutex_unlock(&priv->devlock);
|
|
return ret;
|
|
}
|
|
|
|
/* Hardcoded value specified in the datasheet */
|
|
|
|
usleep(2000);
|
|
}
|
|
|
|
ret = amg88xx_i2c_write_addr(priv,
|
|
(uint8_t)AMG88XX_RESET_REGISTER,
|
|
(uint8_t)AMG88XX_FLAG_RESET_CMD);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nxmutex_unlock(&priv->devlock);
|
|
return ret;
|
|
}
|
|
|
|
nxmutex_unlock(&priv->devlock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: amg88xx_close
|
|
*
|
|
* Description:
|
|
* Character device close call.
|
|
*
|
|
* Input Parameters:
|
|
* filep - Pointer to struct file
|
|
*
|
|
* Returned Value:
|
|
* OK - Sensor device was closed successfully.
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int amg88xx_close(FAR struct file *filep)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct amg88xx_dev_s *priv = inode->i_private;
|
|
int ret;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
ret = nxmutex_lock(&priv->devlock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Set the sensor to sleep operation mode */
|
|
|
|
ret = amg88xx_i2c_write_addr(priv,
|
|
(uint8_t)AMG88XX_POWER_CONTROL_REGISTER,
|
|
(uint8_t)AMG88XX_SLEEP_MODE_CMD);
|
|
|
|
nxmutex_unlock(&priv->devlock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: amg88xx_read
|
|
*
|
|
* Description:
|
|
* Character device read call. Blocks until requested buffer size is full.
|
|
*
|
|
* Input Parameters:
|
|
* filep - Pointer to struct file
|
|
* buffer - Pointer to user buffer
|
|
* buflen - Size of user buffer in bytes
|
|
*
|
|
* Returned Value:
|
|
* Returns the number of bytes written to the buffer.
|
|
* -EINVAL - Supplied buffer length invalid
|
|
*
|
|
****************************************************************************/
|
|
|
|
static ssize_t amg88xx_read(FAR struct file *filep,
|
|
FAR char *buffer,
|
|
size_t buflen)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct amg88xx_dev_s *priv = inode->i_private;
|
|
uint8_t regval = AMG88XX_PIXEL_TABLE_BASE;
|
|
int ret;
|
|
|
|
DEBUGASSERT(priv != NULL);
|
|
|
|
if (buffer == NULL)
|
|
{
|
|
snerr("ERROR: buffer should not be NULL.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (buflen != AMG88XX_PIXELS_ARRAY_LENGTH)
|
|
{
|
|
snerr("ERROR: buflen is not sufficient to store sensor data.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct i2c_msg_s msg[2];
|
|
|
|
msg[0].addr = priv->config->addr;
|
|
msg[0].frequency = priv->config->speed;
|
|
msg[0].flags = I2C_M_NOSTOP;
|
|
msg[0].buffer = ®val;
|
|
msg[0].length = 1;
|
|
|
|
msg[1].addr = priv->config->addr;
|
|
msg[1].frequency = priv->config->speed;
|
|
msg[1].flags = I2C_M_READ;
|
|
msg[1].buffer = (uint8_t *)buffer;
|
|
msg[1].length = buflen;
|
|
|
|
ret = I2C_TRANSFER(priv->i2c, msg, 2);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int amg88xx_ioctl(FAR struct file *filep, int cmd,
|
|
unsigned long arg)
|
|
{
|
|
FAR struct inode *inode = filep->f_inode;
|
|
FAR struct amg88xx_dev_s *priv = inode->i_private;
|
|
int ret = -EINVAL;
|
|
uint8_t regval = 0;
|
|
|
|
ret = nxmutex_lock(&priv->devlock);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
switch (cmd)
|
|
{
|
|
case SNIOC_SET_OPERATIONAL_MODE:
|
|
switch (*(amg88xx_operation_mode_e *)arg)
|
|
{
|
|
case op_mode_normal:
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_POWER_CONTROL_REGISTER,
|
|
(uint8_t)AMG88XX_NORMAL_MODE_CMD);
|
|
break;
|
|
case op_mode_sleep:
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_POWER_CONTROL_REGISTER,
|
|
(uint8_t)AMG88XX_SLEEP_MODE_CMD);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SNIOC_SET_FRAMERATE:
|
|
switch (*(amg88xx_fps_e *)arg)
|
|
{
|
|
case fps_one:
|
|
regval = (AMG88XX_1FPS << AMG88XX_FPS_BIT);
|
|
ret = amg88xx_i2c_write_addr(priv,
|
|
(uint8_t)AMG88XX_FRAME_RATE_REGISTER,
|
|
regval);
|
|
break;
|
|
case fps_ten:
|
|
regval = (AMG88XX_10FPS << AMG88XX_FPS_BIT);
|
|
ret = amg88xx_i2c_write_addr(priv,
|
|
(uint8_t)AMG88XX_FRAME_RATE_REGISTER,
|
|
regval);
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
/* Enabling and disabling the moving average requires following
|
|
* a procedure described in the i2c communication interface manual.
|
|
*/
|
|
|
|
case SNIOC_SET_MOVING_AVG:
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_AVERAGE_MODE_REGISTER,
|
|
(uint8_t)AMG88XX_AVEAGE_MODE_CMD_1);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_AVERAGE_MODE_REGISTER,
|
|
(uint8_t)AMG88XX_AVEAGE_MODE_CMD_2);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_AVERAGE_MODE_REGISTER,
|
|
(uint8_t)AMG88XX_AVEAGE_MODE_CMD_3);
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
regval = (bool)arg ?
|
|
(AMG88XX_MAMOD_ENABLE << AMG88XX_MAMOD_BIT) :
|
|
(AMG88XX_MAMOD_DISABLE << AMG88XX_MAMOD_BIT) ;
|
|
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_AVERAGE_REGISTER,
|
|
regval);
|
|
|
|
if (ret < 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
ret = amg88xx_i2c_write_addr(
|
|
priv,
|
|
(uint8_t)AMG88XX_AVERAGE_MODE_REGISTER,
|
|
(uint8_t)AMG88XX_AVEAGE_MODE_CMD_4);
|
|
break;
|
|
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
nxmutex_unlock(&priv->devlock);
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: amg88xx_register
|
|
*
|
|
* Description:
|
|
* Register the amg88xx character device as 'devpath'
|
|
*
|
|
* Input Parameters:
|
|
* devpath - The full path to the driver to register. E.g., "/dev/irm0"
|
|
* i2c - An instance of the I2C interface to use to communicate with
|
|
* AMG88xx
|
|
* config - Initial configuration of the sensor
|
|
*
|
|
* Returned Value:
|
|
* Zero (OK) on success; a negated errno value on failure.
|
|
*
|
|
****************************************************************************/
|
|
|
|
int amg88xx_register(FAR const char *devpath,
|
|
FAR struct i2c_master_s *i2c,
|
|
FAR struct amg88xx_config_s *config)
|
|
{
|
|
FAR struct amg88xx_dev_s *priv;
|
|
int ret;
|
|
|
|
/* Sanity check */
|
|
|
|
DEBUGASSERT(i2c != NULL);
|
|
DEBUGASSERT(config != NULL);
|
|
|
|
/* Initialize the AMG88xx device structure */
|
|
|
|
priv = (FAR struct amg88xx_dev_s *)
|
|
kmm_malloc(sizeof(struct amg88xx_dev_s));
|
|
|
|
if (!priv)
|
|
{
|
|
snerr("Failed to allocate instance\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->i2c = i2c;
|
|
priv->config = config;
|
|
nxmutex_init(&priv->devlock);
|
|
|
|
/* Register the character driver */
|
|
|
|
ret = register_driver(devpath, &g_amg88xx_fops, 0600, priv);
|
|
|
|
if (ret < 0)
|
|
{
|
|
nxmutex_destroy(&priv->devlock);
|
|
kmm_free(priv);
|
|
snerr("ERROR: Failed to register driver: %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|