You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
939 lines
27 KiB
939 lines
27 KiB
/* |
|
* 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);
|
|
|