| /* |
| * Copyright (c) 2023 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/ipc/icmsg_me.h> |
| |
| #include <string.h> |
| |
| #define SEND_BUF_SIZE CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_SEND_BUF_SIZE |
| #define NUM_EP CONFIG_IPC_SERVICE_BACKEND_ICMSG_ME_NUM_EP |
| |
| #define EVENT_BOUND 0x01 |
| |
| #define HEADER_SIZE (sizeof(icmsg_me_ept_id_t)) |
| |
| static void *icmsg_buffer_to_user_buffer(const void *icmsg_buffer) |
| { |
| return (void *)(((char *)icmsg_buffer) + HEADER_SIZE); |
| } |
| |
| static void *user_buffer_to_icmsg_buffer(const void *user_buffer) |
| { |
| return (void *)(((char *)user_buffer) - HEADER_SIZE); |
| } |
| |
| static size_t icmsg_buffer_len_to_user_buffer_len(size_t icmsg_buffer_len) |
| { |
| return icmsg_buffer_len - HEADER_SIZE; |
| } |
| |
| static size_t user_buffer_len_to_icmsg_buffer_len(size_t user_buffer_len) |
| { |
| return user_buffer_len + HEADER_SIZE; |
| } |
| |
| static void set_ept_id_in_send_buffer(uint8_t *send_buffer, |
| icmsg_me_ept_id_t ept_id) |
| { |
| send_buffer[0] = ept_id; |
| } |
| |
| int icmsg_me_init(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data) |
| { |
| k_event_init(&data->event); |
| k_mutex_init(&data->send_mutex); |
| |
| return 0; |
| } |
| |
| int icmsg_me_open(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, |
| const struct ipc_service_cb *cb, |
| void *ctx) |
| { |
| data->ept_cfg.cb = *cb; |
| data->ept_cfg.priv = ctx; |
| |
| return icmsg_open(conf, &data->icmsg_data, &data->ept_cfg.cb, |
| data->ept_cfg.priv); |
| } |
| |
| void icmsg_me_icmsg_bound(struct icmsg_me_data_t *data) |
| { |
| k_event_post(&data->event, EVENT_BOUND); |
| } |
| |
| void icmsg_me_wait_for_icmsg_bind(struct icmsg_me_data_t *data) |
| { |
| k_event_wait(&data->event, EVENT_BOUND, false, K_FOREVER); |
| } |
| |
| int icmsg_me_set_empty_ept_cfg_slot(struct icmsg_me_data_t *data, |
| const struct ipc_ept_cfg *ept_cfg, |
| icmsg_me_ept_id_t *id) |
| { |
| int i; |
| |
| for (i = 0; i < NUM_EP; i++) { |
| if (data->epts[i] == NULL) { |
| break; |
| } |
| } |
| |
| if (i >= NUM_EP) { |
| return -ENOMEM; |
| } |
| |
| data->epts[i] = ept_cfg; |
| *id = i + 1; |
| return 0; |
| } |
| |
| static int get_ept_cfg_index(icmsg_me_ept_id_t id) |
| { |
| int i = id - 1; |
| |
| if (i >= NUM_EP || i < 0) { |
| return -ENOENT; |
| } |
| |
| return i; |
| } |
| |
| int icmsg_me_set_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id, |
| const struct ipc_ept_cfg *ept_cfg) |
| { |
| int i = get_ept_cfg_index(id); |
| |
| if (i < 0) { |
| return i; |
| } |
| |
| data->epts[i] = ept_cfg; |
| return 0; |
| } |
| |
| int icmsg_me_get_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id, |
| const struct ipc_ept_cfg **ept_cfg) |
| { |
| int i = get_ept_cfg_index(id); |
| |
| if (i < 0) { |
| return i; |
| } |
| |
| *ept_cfg = data->epts[i]; |
| return 0; |
| } |
| |
| void icmsg_me_reset_ept_cfg(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id) |
| { |
| int i = get_ept_cfg_index(id); |
| |
| if (i < 0) { |
| return; |
| } |
| |
| data->epts[i] = NULL; |
| } |
| |
| void icmsg_me_received_data(struct icmsg_me_data_t *data, icmsg_me_ept_id_t id, |
| const void *msg, size_t len) |
| { |
| int r; |
| const struct ipc_ept_cfg *ept; |
| |
| r = icmsg_me_get_ept_cfg(data, id, &ept); |
| if (r < 0) { |
| return; |
| } |
| |
| if (ept == NULL) { |
| return; |
| } |
| |
| if (ept->cb.received) { |
| ept->cb.received(icmsg_buffer_to_user_buffer(msg), |
| icmsg_buffer_len_to_user_buffer_len(len), |
| ept->priv); |
| } |
| } |
| |
| int icmsg_me_send(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, icmsg_me_ept_id_t id, |
| const void *msg, size_t len) |
| { |
| int r; |
| int sent_bytes; |
| |
| if (user_buffer_len_to_icmsg_buffer_len(len) >= SEND_BUF_SIZE) { |
| return -EBADMSG; |
| } |
| |
| k_mutex_lock(&data->send_mutex, K_FOREVER); |
| |
| /* TODO: Optimization: How to avoid this copying? */ |
| /* We could implement scatter list for icmsg_send, but it would require |
| * scatter list also for SPSC buffer implementation. |
| */ |
| set_ept_id_in_send_buffer(data->send_buffer, id); |
| memcpy(icmsg_buffer_to_user_buffer(data->send_buffer), msg, len); |
| |
| r = icmsg_send(conf, &data->icmsg_data, data->send_buffer, |
| user_buffer_len_to_icmsg_buffer_len(len)); |
| if (r > 0) { |
| sent_bytes = icmsg_buffer_len_to_user_buffer_len(r); |
| } |
| |
| k_mutex_unlock(&data->send_mutex); |
| |
| if (r < 0) { |
| return r; |
| } |
| |
| __ASSERT_NO_MSG(r >= HEADER_SIZE); |
| if (r < HEADER_SIZE) { |
| return 0; |
| } |
| |
| return sent_bytes; |
| } |
| |
| static size_t get_buffer_length_to_pass(size_t icmsg_buffer_len) |
| { |
| if (icmsg_buffer_len >= HEADER_SIZE) { |
| return icmsg_buffer_len_to_user_buffer_len(icmsg_buffer_len); |
| } else { |
| return 0; |
| } |
| } |
| |
| int icmsg_me_get_tx_buffer(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, |
| void **buffer, uint32_t *user_len, k_timeout_t wait) |
| { |
| void *icmsg_buffer; |
| int r; |
| size_t icmsg_len; |
| |
| if (!K_TIMEOUT_EQ(wait, K_NO_WAIT)) { |
| return -ENOTSUP; |
| } |
| |
| if (*user_len) { |
| icmsg_len = user_buffer_len_to_icmsg_buffer_len(*user_len); |
| } else { |
| icmsg_len = 0; |
| } |
| |
| r = icmsg_get_tx_buffer(conf, &data->icmsg_data, |
| &icmsg_buffer, &icmsg_len); |
| if (r == -ENOMEM) { |
| *user_len = get_buffer_length_to_pass(icmsg_len); |
| return -ENOMEM; |
| } |
| |
| if (r < 0) { |
| return r; |
| } |
| |
| /* If requested max buffer length (*len == 0) allocated buffer might be |
| * shorter than HEADER_SIZE. In such circumstances drop the buffer |
| * and return error. |
| */ |
| *user_len = get_buffer_length_to_pass(icmsg_len); |
| |
| if (!(*user_len)) { |
| r = icmsg_drop_tx_buffer(conf, &data->icmsg_data, icmsg_buffer); |
| __ASSERT_NO_MSG(!r); |
| return -ENOBUFS; |
| } |
| |
| *buffer = icmsg_buffer_to_user_buffer(icmsg_buffer); |
| return 0; |
| |
| } |
| |
| int icmsg_me_drop_tx_buffer(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, |
| const void *buffer) |
| { |
| const void *buffer_to_drop = user_buffer_to_icmsg_buffer(buffer); |
| |
| return icmsg_drop_tx_buffer(conf, &data->icmsg_data, buffer_to_drop); |
| } |
| |
| int icmsg_me_send_nocopy(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, icmsg_me_ept_id_t id, |
| const void *msg, size_t len) |
| { |
| void *buffer_to_send; |
| size_t len_to_send; |
| int r; |
| int sent_bytes; |
| |
| buffer_to_send = user_buffer_to_icmsg_buffer(msg); |
| len_to_send = user_buffer_len_to_icmsg_buffer_len(len); |
| |
| set_ept_id_in_send_buffer(buffer_to_send, id); |
| |
| r = icmsg_send_nocopy(conf, &data->icmsg_data, |
| buffer_to_send, len_to_send); |
| |
| if (r < 0) { |
| return r; |
| } |
| |
| __ASSERT_NO_MSG(r >= HEADER_SIZE); |
| if (r < HEADER_SIZE) { |
| return 0; |
| } |
| |
| sent_bytes = icmsg_buffer_len_to_user_buffer_len(r); |
| |
| return sent_bytes; |
| } |
| |
| #ifdef CONFIG_IPC_SERVICE_ICMSG_ME_NOCOPY_RX |
| int icmsg_me_hold_rx_buffer(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, void *buffer) |
| { |
| void *icmsg_buffer = user_buffer_to_icmsg_buffer(buffer); |
| |
| return icmsg_hold_rx_buffer(conf, &data->icmsg_data, icmsg_buffer); |
| } |
| |
| int icmsg_me_release_rx_buffer(const struct icmsg_config_t *conf, |
| struct icmsg_me_data_t *data, void *buffer) |
| { |
| void *icmsg_buffer = user_buffer_to_icmsg_buffer(buffer); |
| |
| return icmsg_release_rx_buffer(conf, &data->icmsg_data, icmsg_buffer); |
| } |
| #endif /* CONFIG_IPC_SERVICE_ICMSG_ME_NOCOPY_RX */ |