| /* |
| * Copyright (c) 2022-2023, Intel Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * ARM SiP services implementation. |
| * |
| * |
| * ******** |
| * Overview |
| * ******** |
| * |
| * Typical flow |
| * (1) register a client, service returns a token |
| * (2) client opens a channel, (optionally only allow one channel at one time) |
| * (3) client send request with callback, service returns transaction id |
| * (5) service callback once the transaction complete |
| * (6) client close channel after receive callback |
| * (7) ... repeats (2) to (7) to send more request |
| * (8) unregister the client |
| * |
| * Abort opened channel |
| * (1) for some reasons, client want to terminate the operation |
| * on the opened channel. client may close the channel |
| * without waiting for all transaction being completed |
| * (2) service will proceed to close the channel and set the client |
| * at ABORT state. The client will be not allowed to reopen |
| * the channel until service complete all its associated transactions |
| * and bring the client back to IDLE state. |
| * |
| * callback implementation requirement |
| * (1) the callback is provided by client, it will be called and executed |
| * in sip_svc thread once transaction is completed. |
| * (2) callback is requested to do the following: |
| * - if the client is running with a thread, callback should ensure |
| * the thread is still alive before handle the response |
| * - response data pointer is not retained after the callback function. |
| * thus, the callback should copy the response data when needed. |
| * - callback responsible to free the asynchronous response data memory |
| * space |
| * |
| * |
| * *************************************** |
| * sip_svc service and client overview |
| * *************************************** |
| * ------------------------------------------------------ |
| * Client1 Client2 Client3 ... |
| * Support | * | |
| * multiple | * open | |
| * clients | * channel | |
| * | * | |
| * ------------------------------------------------------ |
| * sip_svc |
| * service |
| * Thread |
| * ---------- |
| * | Create | when receive first request |
| * ---------- |
| * | |
| * | Run |
| * | |
| * ------------------- |
| * --> | Request handler | Process the request, perform smc/hvc |
| * | ------------------- |
| * | | |
| * Resume | | |
| * when | | |
| * receive | -------------------------- |
| * new | | Async response handler | Poll response of async request |
| * request | -------------------------- perform smc/hvc |
| * | | |
| * | | Suspend when all transactions |
| * | | completed without new request |
| * | | |
| * | ------------------ |
| * --- | Suspend Thread | |
| * ------------------ |
| * ------------------------------------------------------ |
| * |
| * *************************************** |
| * sip_svc service ID management |
| * *************************************** |
| * ------------------------------------------------------ |
| * client Client Client |
| * | | |
| * | Register | Send |
| * | | Request |
| * V V |
| * ------------------------------------------------------ |
| * sip_svc ^ ^ |
| * service | Client Token | Transaction ID |
| * | | |
| * --------------------- ----------------------- |
| * | Alloc an client | | Alloc a Transaction | |
| * | placeholder and | | ID for the request | |
| * | generate a unique | ----------------------- |
| * | token for it | | |
| * --------------------- | |
| * | |
| * | Transaction ID |
| * V |
| * ------------------------------------------------------ |
| * EL2/EL3 ^ |
| * firmware | |
| * Return same Transaction ID |
| * ------------------------------------------------------ |
| * |
| */ |
| |
| #include <zephyr/sip_svc/sip_svc.h> |
| #include <zephyr/sip_svc/sip_svc_controller.h> |
| #include <zephyr/drivers/sip_svc/sip_svc_driver.h> |
| #include <zephyr/sys/iterable_sections.h> |
| #include "sip_svc_id_mgr.h" |
| #include <string.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(sip_svc_subsys, CONFIG_ARM_SIP_SVC_SUBSYS_LOG_LEVEL); |
| |
| static uint32_t sip_svc_generate_c_token(void) |
| { |
| uint32_t c_token = k_cycle_get_32(); |
| return c_token; |
| } |
| |
| static inline bool is_sip_svc_controller(void *ct) |
| { |
| if (ct == NULL) { |
| return false; |
| } |
| |
| STRUCT_SECTION_FOREACH(sip_svc_controller, ctrl) { |
| if ((void *)ctrl == ct) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| static uint32_t sip_svc_get_c_idx(struct sip_svc_controller *ctrl, uint32_t c_token) |
| { |
| uint32_t i; |
| |
| if (!ctrl) { |
| return SIP_SVC_ID_INVALID; |
| } |
| |
| for (i = 0; i < ctrl->num_clients; i++) { |
| if (ctrl->clients[i].token == c_token) { |
| return i; |
| } |
| } |
| |
| return SIP_SVC_ID_INVALID; |
| } |
| |
| uint32_t sip_svc_register(void *ct, void *priv_data) |
| { |
| int err; |
| uint32_t c_idx = SIP_SVC_ID_INVALID; |
| |
| if (ct == NULL || !is_sip_svc_controller(ct)) { |
| return SIP_SVC_ID_INVALID; |
| } |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ct; |
| |
| err = k_mutex_lock(&ctrl->data_mutex, K_FOREVER); |
| if (err != 0) { |
| LOG_ERR("Error in acquiring mutex %d", err); |
| return SIP_SVC_ID_INVALID; |
| } |
| |
| c_idx = sip_svc_id_mgr_alloc(ctrl->client_id_pool); |
| if (c_idx != SIP_SVC_ID_INVALID) { |
| ctrl->clients[c_idx].id = c_idx; |
| ctrl->clients[c_idx].token = sip_svc_generate_c_token(); |
| ctrl->clients[c_idx].state = SIP_SVC_CLIENT_ST_IDLE; |
| ctrl->clients[c_idx].priv_data = priv_data; |
| k_mutex_unlock(&ctrl->data_mutex); |
| LOG_INF("Register the client channel 0x%x", ctrl->clients[c_idx].token); |
| return ctrl->clients[c_idx].token; |
| } |
| |
| k_mutex_unlock(&ctrl->data_mutex); |
| return SIP_SVC_ID_INVALID; |
| } |
| |
| int sip_svc_unregister(void *ct, uint32_t c_token) |
| { |
| int err; |
| uint32_t c_idx; |
| |
| if (ct == NULL || !is_sip_svc_controller(ct)) { |
| return -EINVAL; |
| } |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ct; |
| |
| err = k_mutex_lock(&ctrl->data_mutex, K_FOREVER); |
| if (err != 0) { |
| LOG_ERR("Error in acquiring mutex %d", err); |
| return -ENOLCK; |
| } |
| |
| c_idx = sip_svc_get_c_idx(ctrl, c_token); |
| if (c_idx == SIP_SVC_ID_INVALID) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -EINVAL; |
| } |
| |
| if (ctrl->clients[c_idx].id == SIP_SVC_ID_INVALID) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ENODATA; |
| } |
| |
| if (ctrl->clients[c_idx].active_trans_cnt != 0) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -EBUSY; |
| } |
| |
| if (ctrl->clients[c_idx].state != SIP_SVC_CLIENT_ST_IDLE) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ECANCELED; |
| } |
| |
| LOG_INF("Unregister the client channel 0x%x", ctrl->clients[c_idx].token); |
| ctrl->clients[c_idx].id = SIP_SVC_ID_INVALID; |
| ctrl->clients[c_idx].state = SIP_SVC_CLIENT_ST_INVALID; |
| ctrl->clients[c_idx].token = SIP_SVC_ID_INVALID; |
| ctrl->clients[c_idx].priv_data = NULL; |
| sip_svc_id_mgr_free(ctrl->client_id_pool, c_idx); |
| |
| k_mutex_unlock(&ctrl->data_mutex); |
| return 0; |
| } |
| |
| static bool get_timer_status(bool *first_iteration, struct k_timer *timer, k_timeout_t duration) |
| { |
| if (first_iteration == NULL || timer == NULL) { |
| return false; |
| } |
| |
| if (!(*first_iteration)) { |
| /* start the timer using the timeout variable provided and return true*/ |
| k_timer_start(timer, duration, K_NO_WAIT); |
| *first_iteration = true; |
| return true; |
| } else if (K_TIMEOUT_EQ(duration, K_NO_WAIT)) { |
| /* here we will be at second iteration if duration is K_NO_WAIT, return false */ |
| return false; |
| } else if (K_TIMEOUT_EQ(duration, K_FOREVER)) { |
| /* k_timer won't start for K_FOREVER, so return true*/ |
| return true; |
| } else if (k_timer_remaining_get(timer) > 0) { |
| /* return true if timer has not expired */ |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int sip_svc_open(void *ct, uint32_t c_token, k_timeout_t k_timeout) |
| { |
| |
| uint32_t c_idx; |
| int ret; |
| struct k_timer timer; |
| |
| if (ct == NULL || !is_sip_svc_controller(ct)) { |
| return -EINVAL; |
| } |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ct; |
| |
| /* Initialize the timer */ |
| k_timer_init(&timer, NULL, NULL); |
| |
| /** |
| * Run through the loop until the client is in IDLE state. |
| * Then move the client state to open. If the client has any pending transactions, |
| * the client state will be ABORT state.This will only change when the pending |
| * transactions are complete. |
| */ |
| for (bool first_iteration = false; get_timer_status(&first_iteration, &timer, k_timeout); |
| k_usleep(CONFIG_ARM_SIP_SVC_SUBSYS_ASYNC_POLLING_DELAY)) { |
| |
| ret = k_mutex_lock(&ctrl->data_mutex, K_NO_WAIT); |
| if (ret != 0) { |
| LOG_WRN("0x%x didn't get data lock", c_token); |
| continue; |
| } |
| |
| c_idx = sip_svc_get_c_idx(ctrl, c_token); |
| if (c_idx == SIP_SVC_ID_INVALID) { |
| LOG_ERR("Invalid client token"); |
| k_mutex_unlock(&ctrl->data_mutex); |
| k_timer_stop(&timer); |
| return -EINVAL; |
| } |
| |
| /* Check if the state of client is already open state*/ |
| if (ctrl->clients[c_idx].state == SIP_SVC_CLIENT_ST_OPEN) { |
| LOG_DBG("client with token 0x%x is already open", c_token); |
| k_mutex_unlock(&ctrl->data_mutex); |
| k_timer_stop(&timer); |
| return -EALREADY; |
| } |
| |
| /* Check if the state of client is in idle state*/ |
| if (ctrl->clients[c_idx].state != SIP_SVC_CLIENT_ST_IDLE) { |
| LOG_DBG("client with token 0x%x is not idle", c_token); |
| k_mutex_unlock(&ctrl->data_mutex); |
| continue; |
| } |
| |
| #if CONFIG_ARM_SIP_SVC_SUBSYS_SINGLY_OPEN |
| /** |
| * Acquire open lock, when only one client can transact at |
| * a time. |
| */ |
| if (!atomic_cas(&ctrl->open_lock, SIP_SVC_OPEN_UNLOCKED, SIP_SVC_OPEN_LOCKED)) { |
| LOG_DBG("0x%x didn't get open lock, wait for it to be released", c_token); |
| k_mutex_unlock(&ctrl->data_mutex); |
| continue; |
| } |
| #endif |
| |
| /* Make the client state to be open and stop timer*/ |
| ctrl->clients[c_idx].state = SIP_SVC_CLIENT_ST_OPEN; |
| LOG_INF("0x%x successfully opened a connection with sip_svc", c_token); |
| k_mutex_unlock(&ctrl->data_mutex); |
| k_timer_stop(&timer); |
| return 0; |
| } |
| |
| k_timer_stop(&timer); |
| LOG_ERR("Timedout at %s for 0x%x", __func__, c_token); |
| return -ETIMEDOUT; |
| } |
| |
| int sip_svc_close(void *ct, uint32_t c_token, struct sip_svc_request *pre_close_req) |
| { |
| uint32_t c_idx; |
| int err; |
| |
| if (ct == NULL || !is_sip_svc_controller(ct)) { |
| return -EINVAL; |
| } |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ct; |
| |
| /*If pre-close request is provided, send it to lower layers*/ |
| if (pre_close_req != NULL) { |
| err = sip_svc_send(ct, c_token, pre_close_req, NULL); |
| if (err < 0) { |
| LOG_ERR("Error sending pre_close_req : %d", err); |
| return -ENOTSUP; |
| } |
| } |
| |
| err = k_mutex_lock(&ctrl->data_mutex, K_FOREVER); |
| if (err != 0) { |
| LOG_ERR("Error in acquiring lock %d", err); |
| return err; |
| } |
| |
| c_idx = sip_svc_get_c_idx(ctrl, c_token); |
| if (c_idx == SIP_SVC_ID_INVALID) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -EINVAL; |
| } |
| |
| if (ctrl->clients[c_idx].state != SIP_SVC_CLIENT_ST_OPEN) { |
| LOG_ERR("Client is in wrong state %d", ctrl->clients[c_idx].state); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -EPROTO; |
| } |
| |
| if (ctrl->clients[c_idx].active_trans_cnt != 0) { |
| ctrl->clients[c_idx].state = SIP_SVC_CLIENT_ST_ABORT; |
| } else { |
| ctrl->clients[c_idx].state = SIP_SVC_CLIENT_ST_IDLE; |
| } |
| |
| #if CONFIG_ARM_SIP_SVC_SUBSYS_SINGLY_OPEN |
| (void)atomic_set(&ctrl->open_lock, SIP_SVC_OPEN_UNLOCKED); |
| #endif |
| k_mutex_unlock(&ctrl->data_mutex); |
| |
| LOG_INF("Close the client channel 0x%x", ctrl->clients[c_idx].token); |
| return 0; |
| } |
| |
| static void sip_svc_callback(struct sip_svc_controller *ctrl, uint32_t trans_id, |
| struct sip_svc_response *response) |
| { |
| struct sip_svc_id_map_item *trans_id_item; |
| uint64_t data_addr; |
| uint64_t c_idx; |
| int err; |
| |
| if (!ctrl) { |
| return; |
| } |
| |
| LOG_INF("Got response for trans id 0x%x", trans_id); |
| |
| err = k_mutex_lock(&ctrl->data_mutex, K_FOREVER); |
| if (err != 0) { |
| LOG_ERR("Failed to get lock,%d", err); |
| return; |
| } |
| |
| /* Get trans id callback context from map */ |
| trans_id_item = sip_svc_id_map_query_item(ctrl->trans_id_map, trans_id); |
| |
| if (!trans_id_item) { |
| LOG_ERR("Failed to get the entry from database"); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return; |
| } |
| |
| c_idx = (uint64_t)trans_id_item->arg6; |
| __ASSERT(c_idx < ctrl->num_clients, "c_idx shouldn't be greater than ctrl->num_clients"); |
| |
| __ASSERT(ctrl->clients[c_idx].active_trans_cnt != 0, |
| "At this stage active_trans_cnt shouldn't be 0"); |
| --ctrl->clients[c_idx].active_trans_cnt; |
| |
| if (ctrl->clients[c_idx].state == SIP_SVC_CLIENT_ST_OPEN && trans_id_item->arg1) { |
| |
| ((sip_svc_cb_fn)(trans_id_item->arg1))(ctrl->clients[c_idx].token, response); |
| } else { |
| LOG_INF("Resp data is released as the client channel is closed"); |
| /* Free response memory space if callback is skipped.*/ |
| data_addr = |
| (((uint64_t)trans_id_item->arg2) << 32) | ((uint64_t)trans_id_item->arg3); |
| |
| if (data_addr) { |
| k_free((char *)data_addr); |
| } |
| } |
| |
| /* Free trans id */ |
| sip_svc_id_map_remove_item(ctrl->trans_id_map, trans_id); |
| sip_svc_id_mgr_free(ctrl->clients[c_idx].trans_idx_pool, |
| sip_svc_plat_get_trans_idx(ctrl->dev, trans_id)); |
| |
| if (ctrl->clients[c_idx].active_trans_cnt != 0) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return; |
| } |
| |
| if (ctrl->clients[c_idx].state == SIP_SVC_CLIENT_ST_ABORT) { |
| ctrl->clients[c_idx].state = SIP_SVC_CLIENT_ST_IDLE; |
| } |
| |
| k_mutex_unlock(&ctrl->data_mutex); |
| } |
| |
| static int sip_svc_request_handler(struct sip_svc_controller *ctrl) |
| { |
| struct arm_smccc_res res; |
| struct sip_svc_request request; |
| struct sip_svc_response response; |
| uint32_t trans_id; |
| uint32_t cmd_code; |
| uint32_t error_code; |
| |
| if (!ctrl) { |
| LOG_ERR("Error ctrl is NULL"); |
| return -EINVAL; |
| } |
| |
| /** |
| * If transaction's are more than ctrl->max_transactions, |
| * return -EBUSY. |
| */ |
| if (ctrl->active_job_cnt >= ctrl->max_transactions) { |
| return -EBUSY; |
| } |
| |
| if (k_msgq_num_used_get(&ctrl->req_msgq) == 0) { |
| return 0; |
| } |
| |
| if (k_msgq_get(&ctrl->req_msgq, &request, K_NO_WAIT) != 0) { |
| return -EAGAIN; |
| } |
| |
| /* Get command code from request header */ |
| cmd_code = SIP_SVC_PROTO_HEADER_GET_CODE(request.header); |
| |
| /* Get trans_id from request header */ |
| trans_id = SIP_SVC_PROTO_HEADER_GET_TRANS_ID(request.header); |
| |
| /* Process the request, trigger smc/hvc call */ |
| if (cmd_code == SIP_SVC_PROTO_CMD_ASYNC) { |
| sip_svc_plat_update_trans_id(ctrl->dev, &request, trans_id); |
| } |
| |
| /* Increase active job count. Job means communication with |
| * secure monitor firmware |
| */ |
| ++ctrl->active_job_cnt; |
| |
| /* Trigger smc call */ |
| LOG_INF("%s : triggering %s call", __func__, ctrl->method); |
| LOG_DBG("\theader %08x", request.header); |
| LOG_DBG("\tresp_data_addr %08llx", request.resp_data_addr); |
| LOG_DBG("\tresp_data_size %d", request.resp_data_size); |
| LOG_DBG("\tpriv_data %p", request.priv_data); |
| |
| sip_supervisory_call(ctrl->dev, request.a0, request.a1, request.a2, request.a3, request.a4, |
| request.a5, request.a6, request.a7, &res); |
| |
| /* Release async command data dynamic memory */ |
| if (cmd_code == SIP_SVC_PROTO_CMD_ASYNC) { |
| sip_svc_plat_free_async_memory(ctrl->dev, &request); |
| } |
| |
| /* Callback if fail or sync command */ |
| error_code = sip_svc_plat_get_error_code(ctrl->dev, &res); |
| if (error_code != 0 || cmd_code == SIP_SVC_PROTO_CMD_SYNC) { |
| response.header = SIP_SVC_PROTO_HEADER(error_code, trans_id); |
| response.a0 = res.a0; |
| response.a1 = res.a1; |
| response.a2 = res.a2; |
| response.a3 = res.a3; |
| response.resp_data_addr = request.resp_data_addr; |
| response.resp_data_size = request.resp_data_size; |
| response.priv_data = request.priv_data; |
| |
| sip_svc_callback(ctrl, trans_id, &response); |
| |
| __ASSERT(ctrl->active_job_cnt != 0, "ctrl->active_job_cnt cannot be zero here"); |
| --ctrl->active_job_cnt; |
| } else { |
| ++ctrl->active_async_job_cnt; |
| } |
| |
| return -EINPROGRESS; |
| } |
| |
| static int sip_svc_async_response_handler(struct sip_svc_controller *ctrl) |
| { |
| struct sip_svc_id_map_item *trans_id_item; |
| struct sip_svc_response response; |
| uint32_t trans_id; |
| uint64_t data_addr; |
| size_t data_size; |
| int ret; |
| |
| unsigned long a0 = 0; |
| unsigned long a1 = 0; |
| unsigned long a2 = 0; |
| unsigned long a3 = 0; |
| unsigned long a4 = 0; |
| unsigned long a5 = 0; |
| unsigned long a6 = 0; |
| unsigned long a7 = 0; |
| struct arm_smccc_res res; |
| |
| if (!ctrl) { |
| LOG_ERR("controller is NULL"); |
| return -EINVAL; |
| } |
| |
| /* Return if no busy job id */ |
| if (ctrl->active_async_job_cnt == 0) { |
| LOG_INF("Async resp job queue is empty"); |
| return 0; |
| } |
| |
| if (sip_svc_plat_async_res_req(ctrl->dev, &a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7, |
| ctrl->async_resp_data, ctrl->resp_size)) { |
| LOG_ERR("Error during creation of ASYNC polling request"); |
| return -ENOTSUP; |
| } |
| |
| /* Trigger SMC call */ |
| LOG_INF("%s : triggering %s call", __func__, ctrl->method); |
| LOG_DBG("%s (polling async response)", ctrl->method); |
| |
| sip_supervisory_call(ctrl->dev, a0, a1, a2, a3, a4, a5, a6, a7, &res); |
| |
| /* Callback if get response */ |
| ret = sip_svc_plat_async_res_res(ctrl->dev, &res, ctrl->async_resp_data, &data_size, |
| &trans_id); |
| |
| if (ret != 0) { |
| return -EINPROGRESS; |
| } |
| |
| /* get caller information based on trans id */ |
| trans_id_item = sip_svc_id_map_query_item(ctrl->trans_id_map, trans_id); |
| |
| if (!trans_id_item) { |
| LOG_ERR("Failed to get entry from database"); |
| return -ENOENT; |
| } |
| |
| /* Get caller provided memory space to put response */ |
| data_addr = (((uint64_t)trans_id_item->arg3) | ((uint64_t)trans_id_item->arg2) << 32); |
| |
| /* Check caller provided memory space to avoid overflow */ |
| if (data_size > ((size_t)trans_id_item->arg4)) { |
| data_size = ((size_t)trans_id_item->arg4); |
| } |
| |
| response.header = |
| SIP_SVC_PROTO_HEADER(sip_svc_plat_get_error_code(ctrl->dev, &res), trans_id); |
| response.a0 = res.a0; |
| response.a1 = res.a1; |
| response.a2 = res.a2; |
| response.a3 = res.a3; |
| response.resp_data_addr = data_addr; |
| response.resp_data_size = data_size; |
| response.priv_data = trans_id_item->arg5; |
| |
| /* Copy async cmd response into caller given memory space */ |
| if (data_addr) { |
| memcpy((char *)data_addr, ctrl->async_resp_data, data_size); |
| } |
| |
| sip_svc_callback(ctrl, trans_id, &response); |
| |
| __ASSERT(ctrl->active_job_cnt, "ctrl->active_job_cnt cannot be zero here"); |
| --ctrl->active_job_cnt; |
| |
| __ASSERT(ctrl->active_async_job_cnt != 0, "ctrl->active_async_job_cnt cannot be zero here"); |
| --ctrl->active_async_job_cnt; |
| |
| /* Check again is there any async busy job id */ |
| if (ctrl->active_async_job_cnt == 0) { |
| LOG_INF("Async resp job queue is serviced"); |
| return 0; |
| } |
| |
| return -EINPROGRESS; |
| } |
| |
| static void sip_svc_thread(void *ctrl_ptr, void *arg2, void *arg3) |
| { |
| ARG_UNUSED(arg2); |
| ARG_UNUSED(arg3); |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ctrl_ptr; |
| int ret_msgq; |
| int ret_resp; |
| |
| while (1) { |
| ret_msgq = -EINPROGRESS; |
| ret_resp = -EINPROGRESS; |
| while (ret_msgq != 0 || ret_resp != 0) { |
| ret_resp = sip_svc_async_response_handler(ctrl); |
| ret_msgq = sip_svc_request_handler(ctrl); |
| |
| /* sleep only when waiting for ASYNC responses*/ |
| if (ret_msgq == 0 && ret_resp != 0) { |
| k_usleep(CONFIG_ARM_SIP_SVC_SUBSYS_ASYNC_POLLING_DELAY); |
| } |
| } |
| LOG_INF("Suspend thread, all transactions are completed"); |
| k_thread_suspend(ctrl->tid); |
| } |
| } |
| |
| int sip_svc_send(void *ct, uint32_t c_token, struct sip_svc_request *request, sip_svc_cb_fn cb) |
| { |
| uint32_t trans_id = SIP_SVC_ID_INVALID; |
| uint32_t trans_idx = SIP_SVC_ID_INVALID; |
| uint32_t c_idx; |
| int ret; |
| |
| if (ct == NULL || !is_sip_svc_controller(ct) || request == NULL) { |
| return -EINVAL; |
| } |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ct; |
| |
| if (!sip_svc_plat_func_id_valid(ctrl->dev, |
| (uint32_t)SIP_SVC_PROTO_HEADER_GET_CODE(request->header), |
| (uint32_t)request->a0)) { |
| return -EOPNOTSUPP; |
| } |
| |
| ret = k_mutex_lock(&ctrl->data_mutex, K_FOREVER); |
| if (ret != 0) { |
| LOG_ERR("Failed to get lock %d", ret); |
| return -ENOLCK; |
| } |
| |
| c_idx = sip_svc_get_c_idx(ctrl, c_token); |
| if (c_idx == SIP_SVC_ID_INVALID) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -EINVAL; |
| } |
| |
| if (ctrl->clients[c_idx].state != SIP_SVC_CLIENT_ST_OPEN) { |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ESRCH; |
| } |
| |
| /* Allocate a trans id for the request */ |
| trans_idx = sip_svc_id_mgr_alloc(ctrl->clients[c_idx].trans_idx_pool); |
| if (trans_idx == SIP_SVC_ID_INVALID) { |
| LOG_ERR("Fail to allocate transaction id"); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ENOMEM; |
| } |
| |
| trans_id = sip_svc_plat_format_trans_id(ctrl->dev, c_idx, trans_idx); |
| /* Additional check for an unsupported condition*/ |
| if (((int)trans_id) < 0) { |
| LOG_ERR("Unsupported condition, trans_id < 0"); |
| sip_svc_id_mgr_free(ctrl->clients[c_idx].trans_idx_pool, trans_idx); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ENOTSUP; |
| } |
| |
| /* Assign the trans id of this request */ |
| SIP_SVC_PROTO_HEADER_SET_TRANS_ID(request->header, trans_id); |
| |
| /* Map trans id to client, callback, response data addr */ |
| if (sip_svc_id_map_insert_item(ctrl->trans_id_map, trans_id, (void *)cb, |
| (void *)((request->resp_data_addr >> 32) & 0xFFFFFFFF), |
| (void *)(request->resp_data_addr & 0xFFFFFFFF), |
| (void *)(uint64_t)request->resp_data_size, |
| request->priv_data, (void *)(uint64_t)c_idx) != 0) { |
| |
| LOG_ERR("Fail to insert transaction id to map"); |
| sip_svc_id_mgr_free(ctrl->clients[c_idx].trans_idx_pool, trans_idx); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ENOMSG; |
| } |
| |
| /* Insert request to MSGQ */ |
| LOG_INF("send command to msgq"); |
| if (k_msgq_put(&ctrl->req_msgq, (void *)request, K_NO_WAIT) != 0) { |
| LOG_ERR("Request msgq full"); |
| sip_svc_id_map_remove_item(ctrl->trans_id_map, trans_id); |
| sip_svc_id_mgr_free(ctrl->clients[c_idx].trans_idx_pool, trans_idx); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -ENOBUFS; |
| } |
| ++ctrl->clients[c_idx].active_trans_cnt; |
| |
| if (!ctrl->tid) { |
| LOG_ERR("Thread not spawned during init"); |
| sip_svc_id_map_remove_item(ctrl->trans_id_map, trans_id); |
| sip_svc_id_mgr_free(ctrl->clients[c_idx].trans_idx_pool, trans_idx); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return -EHOSTDOWN; |
| } |
| |
| LOG_INF("Wakeup sip_svc thread"); |
| k_thread_resume(ctrl->tid); |
| k_mutex_unlock(&ctrl->data_mutex); |
| |
| return (int)trans_id; |
| } |
| |
| void *sip_svc_get_priv_data(void *ct, uint32_t c_token) |
| { |
| uint32_t c_idx; |
| int err; |
| |
| if (ct == NULL || !is_sip_svc_controller(ct)) { |
| return NULL; |
| } |
| |
| struct sip_svc_controller *ctrl = (struct sip_svc_controller *)ct; |
| |
| err = k_mutex_lock(&ctrl->data_mutex, K_FOREVER); |
| if (err != 0) { |
| LOG_ERR("Failed to get lock %d", err); |
| return NULL; |
| } |
| |
| c_idx = sip_svc_get_c_idx(ctrl, c_token); |
| if (c_idx == SIP_SVC_ID_INVALID) { |
| LOG_ERR("Client id is invalid"); |
| k_mutex_unlock(&ctrl->data_mutex); |
| return NULL; |
| } |
| |
| k_mutex_unlock(&ctrl->data_mutex); |
| return ctrl->clients[c_idx].priv_data; |
| } |
| |
| void *sip_svc_get_controller(char *method) |
| { |
| if (method == NULL) { |
| LOG_ERR("controller is NULL"); |
| return NULL; |
| } |
| |
| /** |
| * For more info on below code check @ref SIP_SVC_CONTROLLER_DEFINE() |
| */ |
| STRUCT_SECTION_FOREACH(sip_svc_controller, ctrl) { |
| if (!strncmp(ctrl->method, method, SIP_SVC_SUBSYS_CONDUIT_NAME_LENGTH)) { |
| return (void *)ctrl; |
| } |
| } |
| |
| LOG_ERR("controller couldn't be found"); |
| return NULL; |
| } |
| |
| static int sip_svc_subsys_init(void) |
| { |
| int ret = 0; |
| uint32_t ctrl_count = 0; |
| char *msgq_buf = NULL; |
| struct device *dev = NULL; |
| struct sip_svc_client *client = NULL; |
| |
| LOG_INF("Start of %s", __func__); |
| |
| STRUCT_SECTION_COUNT(sip_svc_controller, &ctrl_count); |
| __ASSERT(ctrl_count <= 2, "There should be at most 2 controllers"); |
| |
| /** |
| * Get controller array ,Controller which is instantiated by driver using |
| * SIP_SVC_CONTROLLER_DEFINE(),see @ref SIP_SVC_CONTROLLER_DEFINE() for more |
| * info. |
| */ |
| STRUCT_SECTION_FOREACH(sip_svc_controller, ctrl) { |
| if (!device_is_ready(ctrl->dev)) { |
| LOG_ERR("device not ready"); |
| return -ENODEV; |
| } |
| dev = (struct device *)(ctrl->dev); |
| |
| LOG_INF("Got registered conduit %.*s", (int)sizeof(ctrl->method), ctrl->method); |
| |
| ctrl->async_resp_data = k_malloc(ctrl->resp_size); |
| if (ctrl->async_resp_data == NULL) { |
| return -ENOMEM; |
| } |
| |
| ctrl->client_id_pool = sip_svc_id_mgr_create(ctrl->num_clients); |
| if (!ctrl->client_id_pool) { |
| k_free(ctrl->async_resp_data); |
| return -ENOMEM; |
| } |
| |
| ctrl->trans_id_map = sip_svc_id_map_create(ctrl->max_transactions); |
| if (!ctrl->trans_id_map) { |
| sip_svc_id_mgr_delete(ctrl->client_id_pool); |
| k_free(ctrl->async_resp_data); |
| return -ENOMEM; |
| } |
| |
| /* Alloc request msgq ring buffer */ |
| msgq_buf = k_malloc(sizeof(struct sip_svc_request) * |
| CONFIG_ARM_SIP_SVC_SUBSYS_MSGQ_DEPTH); |
| if (!msgq_buf) { |
| sip_svc_id_mgr_delete(ctrl->client_id_pool); |
| sip_svc_id_map_delete(ctrl->trans_id_map); |
| k_free(ctrl->async_resp_data); |
| return -ENOMEM; |
| } |
| |
| ctrl->clients = k_malloc(ctrl->num_clients * sizeof(struct sip_svc_client)); |
| if (ctrl->clients == NULL) { |
| sip_svc_id_mgr_delete(ctrl->client_id_pool); |
| sip_svc_id_map_delete(ctrl->trans_id_map); |
| k_free(msgq_buf); |
| k_free(ctrl->async_resp_data); |
| return -ENOMEM; |
| } |
| |
| memset(ctrl->clients, 0, ctrl->num_clients * sizeof(struct sip_svc_client)); |
| |
| /* Initialize request msgq */ |
| k_msgq_init(&ctrl->req_msgq, msgq_buf, sizeof(struct sip_svc_request), |
| CONFIG_ARM_SIP_SVC_SUBSYS_MSGQ_DEPTH); |
| |
| /* Initialize client contents */ |
| for (uint32_t i = 0; i < ctrl->num_clients; i++) { |
| client = &ctrl->clients[i]; |
| client->id = SIP_SVC_ID_INVALID; |
| client->token = SIP_SVC_ID_INVALID; |
| client->state = SIP_SVC_CLIENT_ST_INVALID; |
| client->active_trans_cnt = 0; |
| |
| client->trans_idx_pool = sip_svc_id_mgr_create( |
| CONFIG_ARM_SIP_SVC_SUBSYS_MAX_TRANSACTION_ID_COUNT); |
| if (!client->trans_idx_pool) { |
| ret = -ENOMEM; |
| break; |
| } |
| } |
| |
| if (ret != 0) { |
| sip_svc_id_mgr_delete(ctrl->client_id_pool); |
| sip_svc_id_map_delete(ctrl->trans_id_map); |
| k_free(msgq_buf); |
| k_free(ctrl->clients); |
| k_free(ctrl->async_resp_data); |
| |
| for (uint32_t i = 0; i < ctrl->num_clients; i++) { |
| client = &ctrl->clients[i]; |
| if (client->trans_idx_pool) { |
| sip_svc_id_mgr_delete(client->trans_idx_pool); |
| } |
| } |
| return ret; |
| } |
| |
| /* Create and run the thread */ |
| ctrl->tid = k_thread_create( |
| &ctrl->thread, ctrl->stack, CONFIG_ARM_SIP_SVC_SUBSYS_THREAD_STACK_SIZE, |
| sip_svc_thread, ctrl, NULL, NULL, CONFIG_ARM_SIP_SVC_SUBSYS_THREAD_PRIORITY, |
| K_ESSENTIAL, K_NO_WAIT); |
| k_thread_name_set(ctrl->tid, "sip_svc"); |
| |
| ctrl->active_job_cnt = 0; |
| ctrl->active_async_job_cnt = 0; |
| |
| /* Initialize atomic variable */ |
| #if CONFIG_ARM_SIP_SVC_SUBSYS_SINGLY_OPEN |
| (void)atomic_set(&ctrl->open_lock, SIP_SVC_OPEN_UNLOCKED); |
| #endif |
| /* Initialize mutex */ |
| k_mutex_init(&ctrl->data_mutex); |
| |
| ctrl->init = true; |
| } |
| |
| LOG_INF("Completed %s", __func__); |
| return 0; |
| } |
| |
| SYS_INIT(sip_svc_subsys_init, POST_KERNEL, CONFIG_ARM_SIP_SVC_SUBSYS_INIT_PRIORITY); |