| /* |
| * Copyright (c) 2025 Bayrem Gharsellaoui |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/init.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <zephyr/crypto/crypto.h> |
| #include <zephyr/drivers/clock_control/stm32_clock_control.h> |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/drivers/reset.h> |
| #include <soc.h> |
| |
| #include "crypto_stm32_hash_priv.h" |
| |
| #define LOG_LEVEL CONFIG_CRYPTO_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(crypto_stm32_hash); |
| |
| #define DT_DRV_COMPAT st_stm32_hash |
| |
| static struct crypto_stm32_hash_session stm32_hash_sessions[CONFIG_CRYPTO_STM32_HASH_MAX_SESSIONS]; |
| |
| static int crypto_stm32_hash_get_unused_session_index(const struct device *dev) |
| { |
| struct crypto_stm32_hash_data *data = CRYPTO_STM32_HASH_DATA(dev); |
| |
| k_sem_take(&data->session_sem, K_FOREVER); |
| |
| for (int i = 0; i < CONFIG_CRYPTO_STM32_HASH_MAX_SESSIONS; i++) { |
| if (!stm32_hash_sessions[i].in_use) { |
| stm32_hash_sessions[i].in_use = true; |
| k_sem_give(&data->session_sem); |
| return i; |
| } |
| } |
| |
| k_sem_give(&data->session_sem); |
| return -1; |
| } |
| |
| static int stm32_hash_handler(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish) |
| { |
| const struct device *dev = ctx->device; |
| struct crypto_stm32_hash_data *data = CRYPTO_STM32_HASH_DATA(dev); |
| struct crypto_stm32_hash_session *session = CRYPTO_STM32_HASH_SESSN(ctx); |
| HAL_StatusTypeDef status; |
| |
| if (!pkt || !pkt->in_buf || !pkt->out_buf) { |
| LOG_ERR("Invalid packet buffers"); |
| return -EINVAL; |
| } |
| |
| if (!finish) { |
| LOG_ERR("Multipart hashing not supported yet"); |
| return -ENOTSUP; |
| } |
| |
| k_sem_take(&data->device_sem, K_FOREVER); |
| |
| switch (session->algo) { |
| case CRYPTO_HASH_ALGO_SHA224: |
| status = HAL_HASHEx_SHA224_Start(&data->hhash, pkt->in_buf, pkt->in_len, |
| pkt->out_buf, HAL_MAX_DELAY); |
| break; |
| case CRYPTO_HASH_ALGO_SHA256: |
| status = HAL_HASHEx_SHA256_Start(&data->hhash, pkt->in_buf, pkt->in_len, |
| pkt->out_buf, HAL_MAX_DELAY); |
| break; |
| default: |
| k_sem_give(&data->device_sem); |
| LOG_ERR("Unsupported algorithm in handler: %d", session->algo); |
| return -EINVAL; |
| } |
| |
| k_sem_give(&data->device_sem); |
| |
| if (status != HAL_OK) { |
| LOG_ERR("HAL HASH computation failed (status=%d)", status); |
| return -EIO; |
| } |
| |
| LOG_DBG("Hash computation successful"); |
| return 0; |
| } |
| |
| static int stm32_hash_begin_session(const struct device *dev, struct hash_ctx *ctx, |
| enum hash_algo algo) |
| { |
| int ctx_idx; |
| struct crypto_stm32_hash_session *session; |
| |
| switch (algo) { |
| case CRYPTO_HASH_ALGO_SHA224: |
| case CRYPTO_HASH_ALGO_SHA256: |
| break; |
| default: |
| LOG_ERR("Unsupported hash algorithm: %d", algo); |
| return -EINVAL; |
| } |
| |
| ctx_idx = crypto_stm32_hash_get_unused_session_index(dev); |
| if (ctx_idx < 0) { |
| LOG_ERR("No free session for now"); |
| return -ENOSPC; |
| } |
| |
| session = &stm32_hash_sessions[ctx_idx]; |
| memset(&session->config, 0, sizeof(session->config)); |
| memset(session->digest, 0, sizeof(session->digest)); |
| session->in_use = true; |
| session->algo = algo; |
| |
| ctx->drv_sessn_state = session; |
| ctx->hash_hndlr = stm32_hash_handler; |
| ctx->started = false; |
| |
| LOG_DBG("begin_session (algo=%d)", algo); |
| return 0; |
| } |
| |
| static int stm32_hash_free_session(const struct device *dev, struct hash_ctx *ctx) |
| { |
| struct crypto_stm32_hash_session *session = CRYPTO_STM32_HASH_SESSN(ctx); |
| |
| if (!session) { |
| LOG_ERR("Tried to free a NULL session"); |
| return -EINVAL; |
| } |
| |
| memset(session, 0, sizeof(*session)); |
| |
| LOG_DBG("Session freed"); |
| return 0; |
| } |
| |
| static int stm32_hash_query_caps(const struct device *dev) |
| { |
| return (CAP_SYNC_OPS | CAP_SEPARATE_IO_BUFS); |
| } |
| |
| static int crypto_stm32_hash_init(const struct device *dev) |
| { |
| const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); |
| const struct crypto_stm32_hash_config *cfg = CRYPTO_STM32_HASH_CFG(dev); |
| struct crypto_stm32_hash_data *data = CRYPTO_STM32_HASH_DATA(dev); |
| |
| if (!device_is_ready(clk)) { |
| LOG_ERR("Clock control device not ready"); |
| return -ENODEV; |
| } |
| |
| if (clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken) != 0) { |
| LOG_ERR("Clock op failed\n"); |
| return -EIO; |
| } |
| |
| k_sem_init(&data->device_sem, 1, 1); |
| k_sem_init(&data->session_sem, 1, 1); |
| |
| data->hhash.Init.DataType = HASH_DATATYPE_8B; |
| if (HAL_HASH_Init(&data->hhash) != HAL_OK) { |
| LOG_ERR("Peripheral init error"); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static DEVICE_API(crypto, stm32_hash_funcs) = { |
| .hash_begin_session = stm32_hash_begin_session, |
| .hash_free_session = stm32_hash_free_session, |
| .query_hw_caps = stm32_hash_query_caps, |
| }; |
| |
| static struct crypto_stm32_hash_data crypto_stm32_hash_dev_data = {0}; |
| |
| static const struct crypto_stm32_hash_config crypto_stm32_hash_dev_config = { |
| .reset = RESET_DT_SPEC_INST_GET(0), |
| .pclken = {.enr = DT_INST_CLOCKS_CELL(0, bits), .bus = DT_INST_CLOCKS_CELL(0, bus)}}; |
| |
| DEVICE_DT_INST_DEFINE(0, crypto_stm32_hash_init, NULL, &crypto_stm32_hash_dev_data, |
| &crypto_stm32_hash_dev_config, POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY, |
| &stm32_hash_funcs); |