drivers/crypto: add Microchip RNG90 driver

Add Microchip RNG90 TRNG driver with board integration for esp32c3 and rng90 defconfig support.

Signed-off-by: Felipe Moura <moura.fmo@gmail.com>
This commit is contained in:
Felipe Moura 2026-06-07 23:23:43 -03:00 committed by Xiang Xiao
parent 03685825c9
commit e5d8959128
11 changed files with 697 additions and 0 deletions

View file

@ -0,0 +1,74 @@
/****************************************************************************
* boards/risc-v/esp32c3/common/include/esp_board_rng90.h
*
* 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.
*
****************************************************************************/
#ifndef __BOARDS_RISCV_ESP32C3_COMMON_INCLUDE_ESP_BOARD_RNG90_H
#define __BOARDS_RISCV_ESP32C3_COMMON_INCLUDE_ESP_BOARD_RNG90_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifndef __ASSEMBLY__
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: board_rng90_initialize
*
* Description:
* Initialize and register the RNG90 True Random Number Generator driver.
*
* Input Parameters:
* devno - The device number, used to build the device path as /dev/rngN
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
****************************************************************************/
#ifdef CONFIG_DEV_RNG90
int board_rng90_initialize(int devno);
#endif /* CONFIG_DEV_RNG90 */
#undef EXTERN
#if defined(__cplusplus)
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __BOARDS_RISCV_ESP32C3_COMMON_INCLUDE_ESP_BOARD_RNG90_H */

View file

@ -76,6 +76,10 @@ if(CONFIG_ARCH_BOARD_COMMON)
list(APPEND SRCS esp_board_bmp280.c)
endif()
if(CONFIG_DEV_RNG90)
list(APPEND SRCS esp_board_rng90.c)
endif()
if(CONFIG_MMCSD_SPI)
list(APPEND SRCS esp_board_mmcsd.c)
endif()

View file

@ -74,6 +74,10 @@ ifeq ($(CONFIG_SENSORS_BMP280),y)
CSRCS += esp_board_bmp280.c
endif
ifeq ($(CONFIG_DEV_RNG90),y)
CSRCS += esp_board_rng90.c
endif
ifeq ($(CONFIG_MMCSD_SPI),y)
CSRCS += esp_board_mmcsd.c
endif

View file

@ -0,0 +1,95 @@
/****************************************************************************
* boards/risc-v/esp32c3/common/src/esp_board_rng90.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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <nuttx/arch.h>
#include <nuttx/crypto/rng90.h>
#include <nuttx/debug.h>
#include <nuttx/i2c/i2c_master.h>
#ifndef CONFIG_ESPRESSIF_I2C_BITBANG
# include "espressif/esp_i2c.h"
#else
# include "espressif/esp_i2c_bitbang.h"
#endif
#include "esp_board_rng90.h"
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: board_rng90_initialize
*
* Description:
* Initialize and register the RNG90 True Random Number Generator driver.
*
* Input Parameters:
* devno - The device number, used to build the device path as /dev/rngN
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
****************************************************************************/
int board_rng90_initialize(int devno)
{
struct i2c_master_s *i2c;
char devpath[16];
int ret;
sninfo("Initializing RNG90\n");
/* Initialize I2C */
#ifndef CONFIG_ESPRESSIF_I2C_BITBANG
i2c = esp_i2cbus_initialize(ESPRESSIF_I2C0);
#else
i2c = esp_i2cbus_bitbang_initialize();
#endif
if (i2c == NULL)
{
snerr("ERROR: Failed to initialize I2C for RNG90\n");
return -ENODEV;
}
/* Register the RNG90 driver at "/dev/rngN" */
snprintf(devpath, sizeof(devpath), "/dev/rng%d", devno);
ret = rng90_register(devpath, i2c, RNG90_I2C_ADDR);
if (ret < 0)
{
snerr("ERROR: Failed to register RNG90 driver: %d\n", ret);
return ret;
}
return OK;
}

View file

@ -0,0 +1,51 @@
#
# This file is autogenerated: PLEASE DO NOT EDIT IT.
#
# You can use "make menuconfig" to make any modifications to the installed .config file.
# You can then do "make savedefconfig" to generate a new defconfig file that includes your
# modifications.
#
# CONFIG_NSH_ARGCAT is not set
# CONFIG_NSH_CMDOPT_HEXDUMP is not set
CONFIG_ARCH="risc-v"
CONFIG_ARCH_BOARD="esp32c3-devkit"
CONFIG_ARCH_BOARD_COMMON=y
CONFIG_ARCH_BOARD_ESP32C3_DEVKIT=y
CONFIG_ARCH_CHIP="esp32c3"
CONFIG_ARCH_CHIP_ESP32C3=y
CONFIG_ARCH_INTERRUPTSTACK=1536
CONFIG_ARCH_IRQ_TO_NDX=y
CONFIG_ARCH_MINIMAL_VECTORTABLE_DYNAMIC=y
CONFIG_ARCH_NUSER_INTERRUPTS=17
CONFIG_ARCH_RISCV=y
CONFIG_ARCH_STACKDUMP=y
CONFIG_BOARDCTL_RESET=y
CONFIG_BOARD_LOOPSPERMSEC=15000
CONFIG_BUILTIN=y
CONFIG_CRYPTO=y
CONFIG_DEV_RNG90=y
CONFIG_ESPRESSIF_I2C0=y
CONFIG_EXAMPLES_RNG90=y
CONFIG_FS_PROCFS=y
CONFIG_IDLETHREAD_STACKSIZE=2048
CONFIG_INIT_ENTRYPOINT="nsh_main"
CONFIG_INTELHEX_BINARY=y
CONFIG_LIBC_PERROR_STDOUT=y
CONFIG_LIBC_STRERROR=y
CONFIG_NFILE_DESCRIPTORS_PER_BLOCK=6
CONFIG_NSH_BUILTIN_APPS=y
CONFIG_NSH_FILEIOSIZE=512
CONFIG_NSH_READLINE=y
CONFIG_NSH_STRERROR=y
CONFIG_PREALLOC_TIMERS=0
CONFIG_RR_INTERVAL=200
CONFIG_SCHED_BACKTRACE=y
CONFIG_SCHED_WAITPID=y
CONFIG_START_DAY=29
CONFIG_START_MONTH=11
CONFIG_START_YEAR=2019
CONFIG_SYSTEM_DUMPSTACK=y
CONFIG_SYSTEM_NSH=y
CONFIG_TESTING_GETPRIME=y
CONFIG_TESTING_OSTEST=y
CONFIG_UART0_SERIAL_CONSOLE=y

View file

@ -41,6 +41,10 @@
#include "esp_board_i2c.h"
#include "esp_board_bmp180.h"
#ifdef CONFIG_DEV_RNG90
# include "esp_board_rng90.h"
#endif
#include "espressif/esp_start.h"
#ifdef CONFIG_ESPRESSIF_ADC
@ -404,6 +408,15 @@ int esp_bringup(void)
}
#endif
#ifdef CONFIG_DEV_RNG90
ret = board_rng90_initialize(0);
if (ret < 0)
{
syslog(LOG_ERR, "Failed to initialize RNG90 driver: %d\n", ret);
}
#endif
#ifdef CONFIG_ESP_SDM
struct esp_sdm_chan_config_s config =
{

View file

@ -23,3 +23,7 @@
if(CONFIG_DEV_URANDOM AND NOT CONFIG_DEV_URANDOM_ARCH)
target_sources(drivers PRIVATE dev_urandom.c)
endif()
if(CONFIG_DEV_RNG90)
target_sources(drivers PRIVATE rng90.c)
endif()

View file

@ -137,3 +137,13 @@ config SE05X_LOG_DEBUG
endchoice # SE05x debug log level
endif #DEV_SE05X
config DEV_RNG90
bool "Enable Microchip RNG90 TRNG"
depends on I2C
depends on CRYPTO
default n
---help---
Enable support for /dev/rng90 random number generator provided by
Microchip RNG90 TRNG.

View file

@ -31,6 +31,10 @@ ifeq ($(CONFIG_DEV_SE05X),y)
include crypto/pnt/Make.defs
endif
ifeq ($(CONFIG_DEV_RNG90),y)
CSRCS += rng90.c
endif
# Include crypto device driver build support
DEPPATH += --dep-path crypto

372
drivers/crypto/rng90.c Normal file
View file

@ -0,0 +1,372 @@
/****************************************************************************
* drivers/crypto/rng90.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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <errno.h>
#include <string.h>
#include <sys/param.h>
#include <nuttx/arch.h>
#include <nuttx/debug.h>
#include <nuttx/mutex.h>
#include <nuttx/crypto/rng90.h>
#include <nuttx/fs/fs.h>
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/kmalloc.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define RNG90_WAKE_I2C_ADDR 0x00
#define RNG90_WAKE_I2C_MAXFREQ 100000
#define RNG90_WAKE_RETRIES 3
#define RNG90_WAKE_DELAY_MS 5
/****************************************************************************
* Private Types
****************************************************************************/
struct rng90_dev_s
{
FAR struct i2c_master_s *i2c;
uint8_t addr;
mutex_t lock;
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static bool rng90_wakeup(FAR struct rng90_dev_s *priv);
static bool rng90_sleep(FAR struct rng90_dev_s *priv);
static void rng90_send_wake_token(FAR struct rng90_dev_s *priv);
static void rng90_i2c_config(FAR struct i2c_config_s *config,
uint32_t frequency, uint8_t address);
static bool rng90_genrnd(FAR struct rng90_dev_s *priv, FAR uint8_t *buffer,
size_t buflen);
/* Character driver methods */
static int rng90_open(FAR struct file *filep);
static int rng90_close(FAR struct file *filep);
static ssize_t rng90_read(FAR struct file *filep, FAR char *buffer,
size_t buflen);
static int rng90_ioctl(FAR struct file *filep, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_rng90_fops =
{
rng90_open, rng90_close, rng90_read, NULL,
NULL, rng90_ioctl, NULL
};
/****************************************************************************
* Private Functions
****************************************************************************/
static int rng90_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct rng90_dev_s *priv = inode->i_private;
/* The close path puts the device into explicit sleep, so wake it here. */
nxmutex_lock(&priv->lock);
if (!rng90_wakeup(priv))
{
crypterr("ERROR: Failed to wake rng90 device\n");
}
nxmutex_unlock(&priv->lock);
return OK;
}
static int rng90_close(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct rng90_dev_s *priv = inode->i_private;
nxmutex_lock(&priv->lock);
if (!rng90_sleep(priv))
{
crypterr("ERROR: Failed to put rng90 device to sleep\n");
}
nxmutex_unlock(&priv->lock);
return OK;
}
static ssize_t rng90_read(FAR struct file *filep, FAR char *buffer,
size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct rng90_dev_s *priv = inode->i_private;
bool ret;
nxmutex_lock(&priv->lock);
ret = rng90_genrnd(priv, (FAR uint8_t *)buffer, buflen);
nxmutex_unlock(&priv->lock);
/* rng90 always returns 32 bytes */
return ret ? (ssize_t)MIN(buflen, 32u) : -EIO;
}
static int rng90_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct rng90_dev_s *priv = inode->i_private;
int ret;
ret = nxmutex_lock(&priv->lock);
if (ret != 0)
{
return ret;
}
switch (cmd)
{
case RNG90_IOC_WAKEUP:
ret = rng90_wakeup(priv) ? OK : ERROR;
break;
case RNG90_IOC_SLEEP:
ret = rng90_sleep(priv) ? OK : ERROR;
break;
case RNG90_IOC_GENRND:
ret = rng90_genrnd(priv, (FAR uint8_t *)arg, 32) ? OK : ERROR;
break;
default:
crypterr("ERROR: Unrecognized cmd: %d\n", cmd);
ret = -ENOTTY;
break;
}
nxmutex_unlock(&priv->lock);
return ret;
}
static void rng90_i2c_config(FAR struct i2c_config_s *config,
uint32_t frequency, uint8_t address)
{
config->frequency = frequency;
config->address = address;
config->addrlen = 7;
}
static bool rng90_is_wake_response(FAR const uint8_t *response)
{
return response[0] == 0x04 && response[1] == 0x11 &&
response[2] == 0x33 && response[3] == 0x43;
}
static void rng90_send_wake_token(FAR struct rng90_dev_s *priv)
{
struct i2c_config_s config;
uint8_t wake_token = 0x01;
uint32_t wake_freq;
/* Wake is an SDA-low pulse, not a normal transaction with an ACK. */
wake_freq = MIN(RNG90_I2C_FREQ, RNG90_WAKE_I2C_MAXFREQ);
rng90_i2c_config(&config, wake_freq, RNG90_WAKE_I2C_ADDR);
i2c_write(priv->i2c, &config, &wake_token, 1);
up_mdelay(RNG90_WAKE_DELAY_MS);
}
static bool rng90_wakeup(FAR struct rng90_dev_s *priv)
{
struct i2c_config_s config;
uint8_t response[4];
int attempt;
int ret;
for (attempt = 0; attempt < RNG90_WAKE_RETRIES; attempt++)
{
rng90_send_wake_token(priv);
rng90_i2c_config(&config, RNG90_I2C_FREQ, priv->addr);
ret = i2c_read(priv->i2c, &config, response, sizeof(response));
if (ret >= 0 && rng90_is_wake_response(response))
{
return true;
}
}
return false;
}
static bool rng90_sleep(FAR struct rng90_dev_s *priv)
{
struct i2c_config_s config;
uint8_t word_address = 0x01;
int ret;
rng90_i2c_config(&config, RNG90_I2C_FREQ, priv->addr);
/* Sleep sequence: device address + word address 0x01 + Stop condition */
ret = i2c_write(priv->i2c, &config, &word_address, 1);
return (ret >= 0);
}
static uint16_t rng90_crc16(FAR const uint8_t *data, size_t len)
{
uint16_t crc = 0x0000;
size_t i;
for (i = 0; i < len; i++)
{
uint8_t byte = data[i];
uint8_t shift;
for (shift = 0x01; shift != 0; shift <<= 1)
{
bool dbit = (byte & shift) != 0;
bool cbit = (crc & 0x8000) != 0;
crc <<= 1;
if (dbit != cbit)
{
crc ^= 0x8005;
}
}
}
return crc;
}
static bool rng90_genrnd(FAR struct rng90_dev_s *priv, FAR uint8_t *buffer,
size_t buflen)
{
struct i2c_config_s config;
uint8_t response[35];
int ret;
int attempt;
/* Random command packet (datasheet section 6.2) */
uint8_t pkt[28];
uint16_t crc;
pkt[0] = 0x03; /* word address: command */
pkt[1] = 27; /* count */
pkt[2] = 0x16; /* opcode: Random */
pkt[3] = 0x00; /* param1 */
pkt[4] = 0x00; /* param2 lo */
pkt[5] = 0x00; /* param2 hi */
/* data: 20 bytes, any value */
memset(&pkt[6], 0x00, 20);
/* CRC over count + packet (pkt[1] through pkt[25]) */
crc = rng90_crc16(&pkt[1], 25);
pkt[26] = (uint8_t)(crc & 0xff); /* CRC lo */
pkt[27] = (uint8_t)(crc >> 8); /* CRC hi */
rng90_i2c_config(&config, RNG90_I2C_FREQ, priv->addr);
for (attempt = 0; attempt < 2; attempt++)
{
if (attempt > 0)
{
rng90_wakeup(priv);
}
ret = i2c_write(priv->i2c, &config, pkt, sizeof(pkt));
if (ret < 0)
{
continue;
}
/* Wait for command execution (datasheet section 5.6.2) */
up_mdelay(72);
/* Response: [count=35][32 random bytes][crc_lo][crc_hi] */
ret = i2c_read(priv->i2c, &config, response, sizeof(response));
if (ret >= 0)
{
memcpy(buffer, &response[1], MIN(buflen, 32u));
return true;
}
}
return false;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int rng90_register(FAR const char *devpath, FAR struct i2c_master_s *i2c,
uint8_t addr)
{
int ret;
FAR struct rng90_dev_s *priv;
/* Sanity check */
DEBUGASSERT(devpath != NULL);
DEBUGASSERT(i2c != NULL);
/* Initialize the device's structure */
priv = (FAR struct rng90_dev_s *)kmm_malloc(sizeof(*priv));
if (priv == NULL)
{
crypterr("ERROR: Failed to allocate instance\n");
return -ENOMEM;
}
priv->i2c = i2c;
priv->addr = addr;
nxmutex_init(&priv->lock);
/* Register the character driver */
ret = register_driver(devpath, &g_rng90_fops, 0666, priv);
if (ret < 0)
{
crypterr("ERROR: Failed to register driver: %d\n", ret);
nxmutex_destroy(&priv->lock);
kmm_free(priv);
return ret;
}
return OK;
}

View file

@ -0,0 +1,66 @@
/****************************************************************************
* include/nuttx/crypto/rng90.h
*
* 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.
*
****************************************************************************/
#ifndef __INCLUDE_NUTTX_CRYPTO_RNG90_H
#define __INCLUDE_NUTTX_CRYPTO_RNG90_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/fs/ioctl.h>
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#if defined(CONFIG_I2C) && defined(CONFIG_DEV_RNG90)
#define RNG90_I2C_ADDR 0x40
#define RNG90_I2C_FREQ 400000
#define _RNG90IOCBASE (0x3b00) /* RNG90 ioctl base */
#define _RNG90IOC(nr) _IOC(_RNG90IOCBASE, nr)
#define RNG90_IOC_WAKEUP _RNG90IOC(0x01)
#define RNG90_IOC_SLEEP _RNG90IOC(0x02)
#define RNG90_IOC_GENRND _RNG90IOC(0x03)
/****************************************************************************
* Public Types
****************************************************************************/
struct i2c_master_s;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
int rng90_register(FAR const char *devpath,
FAR struct i2c_master_s *i2c,
uint8_t addr);
#endif /* CONFIG_I2C && CONFIG_DEV_RNG90 */
#endif /* __INCLUDE_NUTTX_CRYPTO_RNG90_H */