blob: 7bf5d52e133402ed11dbcaf98786ef14c962dcc5 [file] [log] [blame]
/*
* 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_msleep(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.
*/
ret = k_mutex_lock(&ctrl->open_mutex, K_NO_WAIT);
if (ret != 0) {
LOG_DBG("0x%x didn't get open lock, wait for it to be released, %d",
c_token, ret);
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("%x successfully opened a connection with sip_svc", c_token);
k_mutex_unlock(&ctrl->data_mutex);
k_timer_stop(&timer);
return 0;
}
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
k_mutex_unlock(&ctrl->open_mutex);
#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_msleep(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);
if (ctrl->num_clients > CONFIG_ARM_SIP_SVC_SUBSYS_MAX_CLIENT_COUNT) {
LOG_ERR("number of clients cannot be greater than the "
"CONFIG_ARM_SIP_SVC_SUBSYS_MAX_CLIENT_COUNT");
return -EPROTO;
}
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;
}
/* 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 mutex */
#if CONFIG_ARM_SIP_SVC_SUBSYS_SINGLY_OPEN
k_mutex_init(&ctrl->open_mutex);
#endif
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);