| /* |
| * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include "rpc_caller_session.h" |
| #include "util.h" |
| #include <string.h> |
| |
| static rpc_status_t initalize_shared_memory(struct rpc_caller_session *session, |
| struct rpc_caller_interface *caller, |
| size_t shared_memory_size) |
| { |
| if (shared_memory_size) { |
| rpc_status_t status = RPC_ERROR_INTERNAL; |
| |
| status = rpc_caller_create_shared_memory(caller, shared_memory_size, |
| &session->shared_memory); |
| if (status) { |
| rpc_caller_close_session(caller); |
| return status; |
| } |
| |
| session->shared_memory_policy = alloc_for_session; |
| } else { |
| session->shared_memory = (struct rpc_caller_shared_memory){ 0 }; |
| session->shared_memory_policy = alloc_for_each_call; |
| } |
| |
| return RPC_SUCCESS; |
| } |
| |
| rpc_status_t rpc_caller_session_open(struct rpc_caller_session *session, |
| struct rpc_caller_interface *caller, |
| const struct rpc_uuid *service_uuid, |
| uint16_t endpoint_id, |
| size_t shared_memory_size) |
| { |
| rpc_status_t status = RPC_ERROR_INTERNAL; |
| |
| if (!session || !caller || !service_uuid) |
| return RPC_ERROR_INVALID_VALUE; |
| |
| status = rpc_caller_open_session(caller, service_uuid, endpoint_id); |
| if (status) |
| return status; |
| |
| status = initalize_shared_memory(session, caller, shared_memory_size); |
| if (status) |
| return status; |
| |
| session->caller = caller; |
| session->is_call_transaction_in_progress = false; |
| session->request_length = 0; |
| |
| return status; |
| } |
| |
| rpc_status_t rpc_caller_session_find_and_open(struct rpc_caller_session *session, |
| struct rpc_caller_interface *caller, |
| const struct rpc_uuid *service_uuid, |
| size_t shared_memory_size) |
| { |
| rpc_status_t status = RPC_ERROR_INTERNAL; |
| |
| if (!session || !caller || !service_uuid) |
| return RPC_ERROR_INVALID_VALUE; |
| |
| status = rpc_caller_find_and_open_session(caller, service_uuid); |
| if (status) |
| return status; |
| |
| status = initalize_shared_memory(session, caller, shared_memory_size); |
| if (status) |
| return status; |
| |
| session->caller = caller; |
| session->is_call_transaction_in_progress = false; |
| session->request_length = 0; |
| |
| return status; |
| } |
| |
| rpc_status_t rpc_caller_session_close(struct rpc_caller_session *session) |
| { |
| if (!session) |
| return RPC_ERROR_INVALID_VALUE; |
| |
| if (session->is_call_transaction_in_progress) |
| return RPC_ERROR_INVALID_STATE; |
| |
| if (session->shared_memory_policy == alloc_for_session) { |
| rpc_status_t rpc_status = RPC_ERROR_INTERNAL; |
| |
| rpc_status = rpc_caller_release_shared_memory(session->caller, |
| &session->shared_memory); |
| if (rpc_status != RPC_SUCCESS) |
| return rpc_status; |
| } |
| |
| return rpc_caller_close_session(session->caller); |
| } |
| |
| rpc_call_handle rpc_caller_session_begin(struct rpc_caller_session *session, |
| uint8_t **request_buffer, size_t request_length, |
| size_t response_max_length) |
| { |
| rpc_status_t status = RPC_ERROR_INTERNAL; |
| size_t required_buffer_length = MAX(request_length, response_max_length); |
| |
| if (required_buffer_length > UINT32_MAX) |
| return NULL; |
| |
| if (!session || !request_buffer || session->is_call_transaction_in_progress) |
| return NULL; |
| |
| switch (session->shared_memory_policy) { |
| case alloc_for_each_call: |
| if (session->shared_memory.buffer || session->shared_memory.size) |
| return NULL; /* There's already a shared memory */ |
| |
| status = rpc_caller_create_shared_memory(session->caller, required_buffer_length, |
| &session->shared_memory); |
| if (status) |
| return NULL; /* Failed to create shared memory */ |
| break; |
| |
| case alloc_for_session: |
| if (!session->shared_memory.buffer || !session->shared_memory.size) |
| return NULL; /* There's no shared memory */ |
| |
| if (session->shared_memory.size < required_buffer_length) |
| return NULL; /* The allocated shared memory is too small */ |
| break; |
| |
| default: |
| /* Invalid shared memory policy */ |
| return NULL; |
| } |
| |
| *request_buffer = session->shared_memory.buffer; |
| |
| session->is_call_transaction_in_progress = true; |
| session->request_length = request_length; |
| |
| return (rpc_call_handle)session; |
| } |
| |
| rpc_status_t rpc_caller_session_invoke(rpc_call_handle handle, uint32_t opcode, |
| uint8_t **response_buffer, size_t *response_length, |
| service_status_t *service_status) |
| { |
| struct rpc_caller_session *session = (struct rpc_caller_session *)handle; |
| rpc_status_t status = RPC_ERROR_INTERNAL; |
| |
| if (!handle || !response_buffer || !response_length) |
| return RPC_ERROR_INVALID_VALUE; |
| |
| if (!session->is_call_transaction_in_progress) |
| return RPC_ERROR_INVALID_STATE; |
| |
| if (session->request_length && |
| (!session->shared_memory.buffer || !session->shared_memory.size)) |
| return RPC_ERROR_INVALID_STATE; |
| |
| status = rpc_caller_call(session->caller, opcode, &session->shared_memory, |
| session->request_length, response_length, service_status); |
| if (status || *response_length > session->shared_memory.size) { |
| *response_buffer = NULL; |
| *response_length = 0; |
| return status; |
| } |
| |
| *response_buffer = session->shared_memory.buffer; |
| |
| return status; |
| } |
| |
| rpc_status_t rpc_caller_session_end(rpc_call_handle handle) |
| { |
| struct rpc_caller_session *session = (struct rpc_caller_session *)handle; |
| rpc_status_t status = RPC_ERROR_INTERNAL; |
| |
| if (!handle) |
| return RPC_ERROR_INVALID_VALUE; |
| |
| if (!session->is_call_transaction_in_progress) |
| return RPC_ERROR_INVALID_STATE; |
| |
| if (session->request_length && |
| (!session->shared_memory.buffer || !session->shared_memory.size)) |
| return RPC_ERROR_INVALID_STATE; /* There's no shared memory */ |
| |
| switch (session->shared_memory_policy) { |
| case alloc_for_each_call: |
| status = rpc_caller_release_shared_memory(session->caller, |
| &session->shared_memory); |
| if (status) |
| return status; /* Failed to release shared memory */ |
| |
| session->shared_memory = (struct rpc_caller_shared_memory){ 0 }; |
| break; |
| |
| case alloc_for_session: |
| /* Nothing to do */ |
| break; |
| |
| default: |
| return RPC_ERROR_INVALID_STATE; |
| } |
| |
| session->is_call_transaction_in_progress = false; |
| session->request_length = 0; |
| |
| return RPC_SUCCESS; |
| } |