blob: 7657b3b8d0952ff14a8c8c73faa788472ae05301 [file] [log] [blame]
/*
* Copyright (c) 2022-2023, Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*
* A mailbox client command shell on sip_svc service to communicate with SDM.
*/
#include <zephyr/kernel.h>
#include <zephyr/sip_svc/sip_svc.h>
#include <zephyr/drivers/sip_svc/sip_svc_agilex_mailbox.h>
#include <zephyr/drivers/sip_svc/sip_svc_agilex_smc.h>
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define MAX_TIMEOUT_MSECS (1 * 1000UL)
struct private_data {
struct k_sem semaphore;
const struct shell *sh;
};
static void *mb_smc_ctrl;
static uint32_t mb_c_token = SIP_SVC_ID_INVALID;
static int cmd_reg(const struct shell *sh, size_t argc, char **argv)
{
int err;
if (mb_smc_ctrl) {
shell_print(sh, "Mailbox client already registered");
return 0;
}
mb_smc_ctrl = sip_svc_get_controller(argv[1]);
if (!mb_smc_ctrl) {
shell_error(sh, "Arm SiP service %s not found", argv[1]);
return -ENODEV;
}
mb_c_token = sip_svc_register(mb_smc_ctrl, NULL);
if (mb_c_token == SIP_SVC_ID_INVALID) {
mb_smc_ctrl = NULL;
shell_error(sh, "Mailbox client register fail");
err = -1;
} else {
shell_print(sh, "Mailbox client register success (token %08x)", mb_c_token);
err = 0;
}
return err;
}
static int cmd_unreg(const struct shell *sh, size_t argc, char **argv)
{
int err;
if (!mb_smc_ctrl) {
shell_print(sh, "Mailbox client is not registered");
return 0;
}
err = sip_svc_unregister(mb_smc_ctrl, mb_c_token);
if (err) {
shell_error(sh, "Mailbox client unregister fail (%d)", err);
} else {
shell_print(sh, "Mailbox client unregister success");
mb_c_token = SIP_SVC_ID_INVALID;
mb_smc_ctrl = NULL;
}
return err;
}
static int cmd_open(const struct shell *sh, size_t argc, char **argv)
{
unsigned long mseconds = 0;
int err;
char *endptr;
k_timeout_t timeout = K_MSEC(MAX_TIMEOUT_MSECS);
if (!mb_smc_ctrl) {
shell_print(sh, "Mailbox client is not registered");
return 0;
}
if (argc > 1) {
errno = 0;
mseconds = strtoul(argv[1], &endptr, 10);
if (errno == ERANGE) {
shell_error(sh, "out of range value");
return -ERANGE;
} else if (errno || endptr == argv[1] || *endptr) {
return -errno;
} else if (mseconds <= MAX_TIMEOUT_MSECS) {
timeout = K_MSEC(mseconds);
} else {
timeout = K_MSEC(MAX_TIMEOUT_MSECS);
shell_error(sh, "Setting timeout value to %lu milliseconds",
MAX_TIMEOUT_MSECS);
}
}
err = sip_svc_open(mb_smc_ctrl, mb_c_token, timeout);
if (err) {
shell_error(sh, "Mailbox client open fail (%d)", err);
} else {
shell_print(sh, "Mailbox client open success");
}
return err;
}
static int cmd_close(const struct shell *sh, size_t argc, char **argv)
{
int err;
uint32_t cmd_size = sizeof(uint32_t);
struct sip_svc_request request;
if (!mb_smc_ctrl) {
shell_print(sh, "Mailbox client is not registered");
return 0;
}
uint32_t *cmd_addr = k_malloc(cmd_size);
if (!cmd_addr) {
return -ENOMEM;
}
*cmd_addr = MAILBOX_CANCEL_COMMAND;
request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_ASYNC, 0);
request.a0 = SMC_FUNC_ID_MAILBOX_SEND_COMMAND;
request.a1 = 0;
request.a2 = (uint64_t)cmd_addr;
request.a3 = (uint64_t)cmd_size;
request.a4 = 0;
request.a5 = 0;
request.a6 = 0;
request.a7 = 0;
request.resp_data_addr = (uint64_t)NULL;
request.resp_data_size = 0;
request.priv_data = NULL;
err = sip_svc_close(mb_smc_ctrl, mb_c_token, &request);
if (err) {
k_free(cmd_addr);
shell_error(sh, "Mailbox client close fail (%d)", err);
} else {
shell_print(sh, "Mailbox client close success");
}
return err;
}
static void cmd_send_callback(uint32_t c_token, struct sip_svc_response *response)
{
if (response == NULL) {
return;
}
struct private_data *priv = (struct private_data *)response->priv_data;
const struct shell *sh = priv->sh;
uint32_t *resp_data;
uint32_t resp_len;
uint32_t i;
shell_print(sh, "\n\rsip_svc send command callback\n");
shell_print(sh, "\theader=%08x\n", response->header);
shell_print(sh, "\ta0=%016lx\n", response->a0);
shell_print(sh, "\ta1=%016lx\n", response->a1);
shell_print(sh, "\ta2=%016lx\n", response->a2);
shell_print(sh, "\ta3=%016lx\n", response->a3);
shell_print(sh, "\tresponse data=\n");
resp_data = (uint32_t *)response->resp_data_addr;
resp_len = response->resp_data_size / 4;
if (resp_data && resp_len) {
for (i = 0; i < resp_len; i++) {
shell_print(sh, "\t\t[%4d] %08x\n", i, resp_data[i]);
}
} else {
shell_error(sh, "\t\tInvalid addr (%p) or len (%d)\n", resp_data, resp_len);
}
/* Client only responsible to free the response data memory space,
* the command data memory space had been freed by sip_svc service.
*/
if (resp_data) {
shell_print(sh, "response data %p is freed\n", resp_data);
k_free((char *)resp_data);
}
k_sem_give(&(priv->semaphore));
}
static int parse_mb_data(const struct shell *sh, char *hex_list, char **cmd_addr,
uint32_t *cmd_size)
{
char *hex_str = hex_list;
uint32_t hex_val;
uint32_t *buffer;
uint32_t i = 0;
char *state;
char *endptr;
if (!hex_list || !cmd_addr || !cmd_size) {
return -EINVAL;
}
*cmd_addr = k_malloc(SIP_SVP_MB_MAX_WORD_SIZE * 4);
if (!*cmd_addr) {
shell_error(sh, "Fail to allocate command memory");
return -ENOMEM;
}
buffer = (uint32_t *)*cmd_addr;
hex_str = strtok_r(hex_str, " ", &state);
while (hex_str) {
if (i >= SIP_SVP_MB_MAX_WORD_SIZE) {
k_free(*cmd_addr);
shell_error(sh, "Mailbox length too long");
return -EOVERFLOW;
}
errno = 0;
hex_val = strtoul(hex_str, &endptr, 16);
if (errno == ERANGE) {
shell_error(sh, " Value is out of range value");
k_free(*cmd_addr);
return -ERANGE;
} else if (errno || endptr == hex_str || *endptr) {
k_free(*cmd_addr);
shell_error(sh, " Invalid argument");
return -EINVAL;
}
buffer[i] = hex_val;
i++;
hex_str = strtok_r(NULL, " ", &state);
}
*cmd_size = i * 4;
return 0;
}
static int cmd_send(const struct shell *sh, size_t argc, char **argv)
{
struct sip_svc_request request;
int trans_id;
uint32_t cmd_size = 0;
struct private_data priv;
char *cmd_addr;
char *resp_addr, *endptr;
int err;
k_timeout_t timeout = K_FOREVER;
unsigned long msecond = 0;
if (!mb_smc_ctrl) {
shell_print(sh, "Mailbox client is not registered");
return 0;
}
err = parse_mb_data(sh, argv[1], &cmd_addr, &cmd_size);
if (err < 0) {
return err;
}
if (argc > 2) {
errno = 0;
msecond = strtoul(argv[2], &endptr, 10);
if (errno == ERANGE) {
shell_error(sh, "Out of range value");
return -ERANGE;
} else if (errno || endptr == argv[2] || *endptr) {
shell_error(sh, "Invalid argument");
return -EINVAL;
} else if (msecond <= (MSEC_PER_SEC * MAX_TIMEOUT_MSECS)) {
timeout = K_MSEC(msecond);
} else {
timeout = K_SECONDS(MAX_TIMEOUT_MSECS);
shell_error(sh, "Setting timeout value to %lu seconds", MAX_TIMEOUT_MSECS);
}
}
resp_addr = k_malloc(SIP_SVP_MB_MAX_WORD_SIZE * 4);
if (!resp_addr) {
k_free(cmd_addr);
shell_error(sh, "Fail to allocate response memory");
return -ENOMEM;
}
shell_print(sh, "\tResponse memory %p\n", (char *)resp_addr);
k_sem_init(&(priv.semaphore), 0, 1);
priv.sh = sh;
request.header = SIP_SVC_PROTO_HEADER(SIP_SVC_PROTO_CMD_ASYNC, 0);
request.a0 = SMC_FUNC_ID_MAILBOX_SEND_COMMAND;
request.a1 = 0;
request.a2 = (uint64_t)cmd_addr;
request.a3 = (uint64_t)cmd_size;
request.a4 = 0;
request.a5 = 0;
request.a6 = 0;
request.a7 = 0;
request.resp_data_addr = (uint64_t)resp_addr;
request.resp_data_size = SIP_SVP_MB_MAX_WORD_SIZE * 4;
request.priv_data = (void *)&priv;
trans_id = sip_svc_send(mb_smc_ctrl, mb_c_token, &request, cmd_send_callback);
if (trans_id < 0) {
shell_error(sh, "Mailbox send fail (no open or no free trans_id)");
k_free(cmd_addr);
k_free(resp_addr);
err = -EBUSY;
} else {
/*wait for callback*/
if (!k_sem_take(&(priv.semaphore), timeout)) {
shell_print(sh, "Mailbox send success: trans_id %d", trans_id);
err = 0;
} else {
shell_error(sh, "Mailbox send timeout: trans_id %d", trans_id);
cmd_close(sh, 0, NULL);
err = -ETIMEDOUT;
}
}
return err;
}
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_mailbox, SHELL_CMD_ARG(reg, NULL, "<service>", cmd_reg, 2, 0),
SHELL_CMD_ARG(unreg, NULL, NULL, cmd_unreg, 1, 0),
SHELL_CMD_ARG(open, NULL, "[<timeout_msec>]", cmd_open, 1, 1),
SHELL_CMD_ARG(close, NULL, NULL, cmd_close, 1, 0),
SHELL_CMD_ARG(send, NULL,
"<hex list, example (SYNC): \"2001 11223344 aabbccdd\"> [<timeout_msec>]",
cmd_send, 2, 1),
SHELL_SUBCMD_SET_END);
SHELL_CMD_REGISTER(mailbox, &sub_mailbox, "Intel SoC FPGA SDM mailbox client commands", NULL);