nuttx/drivers/leds/max7219.c
Catalin Visinescu 081e4c478a drivers/: Multiple Drivers Are Registered With World Writable - Part 2
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>
2026-07-15 15:27:28 +08:00

361 lines
9.5 KiB
C

/****************************************************************************
* drivers/leds/max7219.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.
*
****************************************************************************/
/* Maxim MAX7219 driver used to control 7-segment display. */
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdlib.h>
#include <errno.h>
#include <nuttx/debug.h>
#include <nuttx/kmalloc.h>
#include <nuttx/fs/fs.h>
#include <nuttx/spi/spi.h>
#include <nuttx/leds/max7219.h>
#if defined(CONFIG_SPI) && defined(CONFIG_LEDS_MAX7219)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* SPI frequency */
#ifndef CONFIG_MAX7219_FREQUENCY
# define CONFIG_MAX7219_FREQUENCY 5000000
#endif
#ifndef CONFIG_MAX7219_INTENSITY
# define CONFIG_MAX7219_INTENSITY 8
#endif
/****************************************************************************
* Private Types
****************************************************************************/
struct max7219_dev_s
{
FAR struct spi_dev_s *spi; /* SPI interface */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline void max7219_configspi(FAR struct spi_dev_s *spi);
static inline void max7219_write16(FAR struct max7219_dev_s *priv,
uint16_t value);
/* Character driver methods */
static ssize_t max7219_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static ssize_t max7219_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_max7219fops =
{
NULL, /* open */
NULL, /* close */
max7219_read, /* read */
max7219_write, /* write */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: max7219_configspi
*
* Description:
* Set the SPI bus configuration
*
****************************************************************************/
static inline void max7219_configspi(FAR struct spi_dev_s *spi)
{
/* Configure SPI for the MAX7219 */
SPI_SETMODE(spi, SPIDEV_MODE0);
SPI_SETBITS(spi, 8);
SPI_HWFEATURES(spi, 0);
SPI_SETFREQUENCY(spi, CONFIG_MAX7219_FREQUENCY);
}
/****************************************************************************
* Name: max7219_write16
*
* Description:
* Write 16-bit to MAX7219
*
****************************************************************************/
static inline void max7219_write16(FAR struct max7219_dev_s *priv,
uint16_t value)
{
/* If SPI bus is shared then lock and configure it */
SPI_LOCK(priv->spi, true);
/* Configure the SPI */
max7219_configspi(priv->spi);
/* Select */
SPI_SELECT(priv->spi, SPIDEV_DISPLAY(0), true);
/* Send 16 bits (2 bytes) */
SPI_SNDBLOCK(priv->spi, &value, 2);
/* De-select */
SPI_SELECT(priv->spi, SPIDEV_DISPLAY(0), false);
/* Unlock bus */
SPI_LOCK(priv->spi, false);
}
/****************************************************************************
* Name: max7219_read
****************************************************************************/
static ssize_t max7219_read(FAR struct file *filep,
FAR char *buffer, size_t buflen)
{
return -ENOSYS;
}
/****************************************************************************
* Name: max7219_write
****************************************************************************/
static ssize_t max7219_write(FAR struct file *filep, FAR const char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct max7219_dev_s *priv = inode->i_private;
uint16_t data;
uint8_t digits[8];
uint8_t digcnt = 0;
uint8_t i;
if (buffer == NULL)
{
lederr("ERROR: Buffer is null\n");
return -1;
}
/* We need at least one display, so 1 byte */
if (buflen < 1)
{
lederr("ERROR: You need to control at least 1 digit!\n");
return -1;
}
/* Get and count the valid digits */
for (i = 0; i < buflen; i++)
{
if (buffer[i] >= '0' && buffer[i] <= '9')
{
digits[digcnt] = buffer[i] - '0';
digcnt++;
}
else
{
switch (buffer[i])
{
case '-':
digits[digcnt] = 0x0a;
digcnt++;
break;
case 'E':
digits[digcnt] = 0x0b;
digcnt++;
break;
case 'H':
digits[digcnt] = 0x0c;
digcnt++;
break;
case 'L':
digits[digcnt] = 0x0d;
digcnt++;
break;
case 'P':
digits[digcnt] = 0x0e;
digcnt++;
break;
case ' ':
digits[digcnt] = 0x0f;
digcnt++;
break;
case '.':
if (i == 0)
{
digits[0] = 0x0f | 0x80;
digcnt++;
}
else
{
digits[digcnt - 1] |= 0x80;
}
break;
default:
lederr("Digit code unsupported\n");
break;
}
}
/* If we reach 8 digits then stop */
if (digcnt >= 8 && buffer[i + 1] != '.')
{
ledwarn("ERROR: The limit is 8 digits!\n");
break;
}
}
/* Blank the display */
for (i = 0; i < 8; i++)
{
data = (MAX7219_DIGIT0 + i) | (0x0f << 8);
max7219_write16(priv, data);
}
/* Write each digit */
for (i = 0; i < digcnt; i++)
{
data = (MAX7219_DIGIT0 + i) | (digits[digcnt - i - 1]) << 8;
max7219_write16(priv, data);
}
/* All bytes were written */
return buflen;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: max7219_leds_register
*
* Description:
* Register the MAX7219 character device as 'devpath'
*
* Input Parameters:
* devpath - The full path to the driver to register. E.g., "/dev/temp0"
* i2c - An instance of the I2C interface to use to communicate with
* MAX7219
* addr - The I2C address of the LM-75. The base I2C address of the
* MAX7219 is 0x48. Bits 0-3 can be controlled to get 8 unique
* addresses from 0x48 through 0x4f.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
int max7219_leds_register(FAR const char *devpath, FAR struct spi_dev_s *spi)
{
FAR struct max7219_dev_s *priv;
uint16_t data;
int ret;
int i;
/* Initialize the MAX7219 device structure */
priv = (FAR struct max7219_dev_s *)
kmm_malloc(sizeof(struct max7219_dev_s));
if (!priv)
{
lederr("ERROR: Failed to allocate instance\n");
return -ENOMEM;
}
priv->spi = spi;
/* Setup defined intensity */
data = (MAX7219_INTENSITY) |
(DISPLAY_INTENSITY(CONFIG_MAX7219_INTENSITY) << 8);
max7219_write16(priv, data);
/* Enable decoding for all digits */
data = (MAX7219_DECODE_MODE) | (ENABLE_DECODE << 8);
max7219_write16(priv, data);
/* Display all digits */
data = (MAX7219_SCAN_LIMIT) | (0x07 << 8);
max7219_write16(priv, data);
/* Leave the shutdown mode */
data = (MAX7219_SHUTDOWN) | (MAX7219_POWER_ON << 8);
max7219_write16(priv, data);
/* Blank the display */
for (i = 0; i < 8; i++)
{
data = (MAX7219_DIGIT0 + i) | (0x0f << 8);
max7219_write16(priv, data);
}
/* Register the character driver */
ret = register_driver(devpath, &g_max7219fops, 0600, priv);
if (ret < 0)
{
lederr("ERROR: Failed to register driver: %d\n", ret);
kmm_free(priv);
}
return ret;
}
#endif /* CONFIG_SPI && CONFIG_MAX7219 */