singe/thirdparty/rockchip-mpp/mpp/mpp_sys.c
2026-09-10 20:17:28 -05:00

781 lines
19 KiB
C

/* SPDX-License-Identifier: Apache-2.0 OR MIT */
/*
* Copyright (c) 2015 Rockchip Electronics Co., Ltd.
*/
#define MODULE_TAG "mpp_sys"
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_2str.h"
#include "mpp_debug.h"
#include "mpp_common.h"
#include "mpp_thread.h"
#include "mpp_singleton.h"
#include "mpp_sys.h"
#include "mpp_cfg_io.h"
#define SYS_DBG_EN (0x00000001)
#define SYS_DBG_FLOW (0x00000002)
#define SYS_DBG_CMD (0x00000010)
#define SYS_DBG_ARGS (0x00000020)
#define SYS_DBG_SPLIT (0x00000040)
#define mpp_sys_dbg(flag, fmt, ...) mpp_dbg(mpp_sys_debug, flag, fmt, ## __VA_ARGS__)
#define sys_dbg_flow(fmt, ...) mpp_sys_dbg(SYS_DBG_FLOW, fmt, ## __VA_ARGS__)
#define sys_dbg_cmd(fmt, ...) mpp_sys_dbg(SYS_DBG_CMD, fmt, ## __VA_ARGS__)
#define sys_dbg_args(fmt, ...) mpp_sys_dbg(SYS_DBG_ARGS, fmt, ## __VA_ARGS__)
#define sys_dbg_split(fmt, ...) mpp_sys_dbg(SYS_DBG_SPLIT, fmt, ## __VA_ARGS__)
#define get_srv_sys() \
({ \
MppSysSrv *__tmp; \
if (srv_sys) { \
__tmp = srv_sys; \
} else { \
mpp_err("mpp sys srv not init at %s\n", __FUNCTION__); \
__tmp = NULL; \
} \
__tmp; \
})
typedef enum MppSysCmdType_e {
MPP_CMD_SYS,
MPP_CMD_VENC,
MPP_CMD_VDEC,
} MppSysCmdType;
typedef struct MppSysSrv_t {
struct list_head list;
MppSThd thd;
rk_s32 pid;
rk_s32 ctx_id;
rk_s32 ctx_cnt;
rk_s32 exit_efd;
} MppSysSrv;
typedef struct MppSysCmdInfo_t MppSysCmdInfo;
struct MppSysCmdInfo_t {
const char *name;
const char *desc;
rk_s32 (*cmd)(MppSysSrv *srv, const char **argv, void *ctx);
MppSysCmdInfo *sub_cmd[];
};
static MppSysSrv *srv_sys = NULL;
static rk_u32 mpp_sys_debug = 0;
rk_s32 mpp_sys_attach(MpiImpl *ctx)
{
MppSysSrv *srv = get_srv_sys();
if (!srv || !srv->thd)
return rk_nok;
INIT_LIST_HEAD(&ctx->list);
mpp_sthd_lock(srv->thd);
list_add_tail(&ctx->list, &srv->list);
ctx->ctx_id = srv->ctx_id++;
if (srv->ctx_id < 0)
srv->ctx_id = 0;
srv->ctx_cnt++;
mpp_sthd_unlock(srv->thd);
return rk_ok;
}
rk_s32 mpp_sys_detach(MpiImpl *ctx)
{
MppSysSrv *srv = get_srv_sys();
if (!srv || !srv->thd)
return rk_nok;
mpp_sthd_lock(srv->thd);
list_del_init(&ctx->list);
srv->ctx_cnt--;
mpp_sthd_unlock(srv->thd);
return rk_ok;
}
static rk_s32 split_line(char *buf, rk_s32 buf_len, const char *argv[], rk_s32 maxargv)
{
rk_s32 argc = 0;
rk_s32 idx = 0;
rk_s32 end = buf_len;
rk_s32 i;
sys_dbg_split("==== split_line: buf_len %d hex_dump:\n", end);
for (i = 0; i < end; i++)
sys_dbg_split(" [%02d] %02x '%c'\n", i, (unsigned char)buf[i],
isprint(buf[i]) ? buf[i] : '?');
while (argc < maxargv && idx < end) {
sys_dbg_split("---- new token loop -> idx %d\n", idx);
while (idx < end && isspace((unsigned char)buf[idx])) {
sys_dbg_split(" skip space @%d char %02x",
idx, (unsigned char)buf[idx]);
idx++;
}
if (idx >= end)
break;
bool in_quote = false;
char quote_char = 0;
rk_s32 token_start = -1;
rk_s32 dst = idx;
sys_dbg_split(" token body start: idx %2d char %02x\n",
idx, (unsigned char)buf[idx]);
while (idx < end) {
char ch = buf[idx];
sys_dbg_split(" idx %2d raw %02x ch '%c' in_quote %d",
idx, (unsigned char)ch, isprint(ch) ? ch : '?', in_quote);
if (!in_quote && isspace((unsigned char)ch)) {
if (token_start == -1) {
idx++;
continue;
}
sys_dbg_split(" break by space @%d", idx);
break;
}
if (ch == '"' || ch == '\'') {
if (!in_quote) {
in_quote = true;
quote_char = ch;
idx++;
continue;
}
if (ch == quote_char) {
in_quote = false;
idx++;
continue;
}
} else if (ch == '\\' && in_quote) {
idx++;
if (idx >= end)
break;
ch = buf[idx];
sys_dbg_split(" escape next %02x", (unsigned char)ch);
if (token_start == -1)
token_start = dst;
buf[dst++] = ch;
idx++;
continue;
}
if (in_quote || !isspace((unsigned char)ch)) {
if (token_start == -1)
token_start = dst;
buf[dst++] = ch;
idx++;
} else {
idx++;
}
}
if (dst < end) {
buf[dst] = '\0';
idx++;
}
sys_dbg_split(" token_end %d token_start %d", dst, token_start);
if (token_start != -1 && token_start < end)
argv[argc++] = &buf[token_start];
sys_dbg_split(" argv[%d] = \"%s\"", argc - 1, &buf[token_start]);
}
sys_dbg_split("==== final argc %d\n", argc);
return argc;
}
static rk_s32 mpp_sys_help(MppSysSrv *srv, const char **argv, void *ctx);
static rk_s32 mpp_sys_show(MppSysSrv *srv, const char **argv, void *ctx);
static rk_s32 mpp_sys_venc(MppSysSrv *srv, const char **argv, void *ctx);
static rk_s32 mpp_sys_venc_dump_cfg(MppSysSrv *srv, const char **argv, void *ctx);
static rk_s32 mpp_sys_venc_show_cfg(MppSysSrv *srv, const char **argv, void *ctx);
static MppSysCmdInfo venc_dump_cfg = {
.name = "dump_cfg",
.desc = "dump venc currnt config to file",
.cmd = mpp_sys_venc_dump_cfg,
.sub_cmd = {
NULL,
},
};
static MppSysCmdInfo venc_show_cfg = {
.name = "show_cfg",
.desc = "show all / enc / dec mpp instances",
.cmd = mpp_sys_venc_show_cfg,
.sub_cmd = {
NULL,
},
};
static MppSysCmdInfo sys_help = {
.name = "help",
.desc = "show all commands",
.cmd = mpp_sys_help,
.sub_cmd = {
NULL,
},
};
static MppSysCmdInfo sys_show = {
.name = "show",
.desc = "show all / enc / dec mpp instances",
.cmd = mpp_sys_show,
.sub_cmd = {
NULL,
},
};
static MppSysCmdInfo venc_cmds = {
.name = "venc",
.desc = "venc command: [all / id] [cmd]",
.cmd = mpp_sys_venc,
.sub_cmd = {
&venc_dump_cfg,
&venc_show_cfg,
NULL,
},
};
static MppSysCmdInfo *sys_cmds[] = {
&sys_help,
&sys_show,
&venc_cmds,
NULL,
};
static rk_s32 mpp_sys_help(MppSysSrv *srv, const char *argv[], void *ctx)
{
MppSysCmdInfo **cmds = &sys_cmds[0];
rk_s32 idx = 0;
(void)srv;
(void)argv;
(void)ctx;
mpp_logi("mpp system command list:");
while (*cmds) {
MppSysCmdInfo *info = *cmds;
MppSysCmdInfo **subs = &info->sub_cmd[0];
rk_s32 sub_idx = 0;
mpp_logi("%2d | %-8s | : %s", idx, info->name, info->desc);
while (*subs) {
MppSysCmdInfo *sub = *subs;
mpp_logi(" |- %2d | %-8s | : %s", sub_idx, sub->name, sub->desc);
subs++;
sub_idx++;
}
cmds++;
idx++;
}
return 0;
}
static rk_s32 mpp_sys_show(MppSysSrv *srv, const char *argv[], void *ctx)
{
MpiImpl *impl;
MpiImpl *tmp;
rk_u32 show_mask = 0;
rk_s32 next = 0;
(void)ctx;
if (argv[1]) {
rk_s32 i;
static const char *filter[] = {
"dec",
"enc",
"all",
};
static rk_u32 filter_mask[] = {
MPP_BIT(MPP_CTX_DEC),
MPP_BIT(MPP_CTX_ENC),
MPP_BIT32_OR(MPP_CTX_ENC, MPP_CTX_DEC),
};
for (i = 0; i < MPP_ARRAY_ELEMS_S(filter); i++) {
if (!strcmp(argv[1], filter[i])) {
show_mask = filter_mask[i];
next++;
mpp_logi("show %s contexts info:\n", filter[i]);
break;
}
}
}
if (show_mask == 0) {
show_mask = MPP_BIT32_OR(MPP_CTX_ENC, MPP_CTX_DEC);
mpp_logi("show all contexts info:\n");
}
mpp_sthd_lock(srv->thd);
list_for_each_entry_safe(impl, tmp, &srv->list, MpiImpl, list) {
rk_u32 mask = MPP_BIT(impl->type);
if ((show_mask & mask) == 0)
continue;
switch (impl->type) {
case MPP_CTX_DEC : {
Mpp *mpp = impl->ctx;
MppDec dec = mpp->mDec;
MppEncCfg cfg = mpp_dec_to_cfg(dec);
rk_s32 width = 0;
rk_s32 height = 0;
mpp_dec_cfg_get_s32(cfg, "status:width", &width);
mpp_dec_cfg_get_s32(cfg, "status:height", &height);
mpp_logi("ctx %4d - dec %-6s - %4dx%-4d\n", impl->ctx_id,
strof_coding_type(impl->coding), width, height);
} break;
case MPP_CTX_ENC : {
Mpp *mpp = impl->ctx;
MppEnc enc = mpp->mEnc;
MppEncCfg cfg = mpp_enc_to_cfg(enc);
rk_s32 width = 0;
rk_s32 height = 0;
mpp_enc_cfg_get_s32(cfg, "prep:width", &width);
mpp_enc_cfg_get_s32(cfg, "prep:height", &height);
mpp_logi("ctx %4d - enc %-6s - %4dx%-4d\n", impl->ctx_id,
strof_coding_type(impl->coding), width, height);
} break;
default : {
} break;
}
}
mpp_sthd_unlock(srv->thd);
return next;
}
static rk_s32 safe_atoi(const char *s, rk_s32 *out)
{
char *e = NULL;
rk_long v;
errno = 0;
v = strtol(s, &e, 10);
if (e == s || *e != '\0')
return -1;
if (errno == ERANGE || v < INT_MIN || v > INT_MAX)
return -2;
*out = (int)v;
return rk_ok;
}
static rk_s32 mpp_sys_venc(MppSysSrv *srv, const char **argv, void *ctx)
{
const char *cmd = NULL;
MppSysCmdInfo *sub = NULL;
MpiImpl *impl;
MpiImpl *tmp;
rk_s32 next = 0;
rk_s32 ctx_id = -1;
(void)ctx;
if (argv[1]) {
rk_s32 ret = safe_atoi(argv[1], &ctx_id);
if (ret == 0 && ctx_id >= 0)
next++;
else
ctx_id = -1;
}
if (argv[next + 1]) {
MppSysCmdInfo **cmds = &venc_cmds.sub_cmd[0];
rk_s32 j = 0;
cmd = argv[next + 1];
while (*cmds) {
MppSysCmdInfo *t = *cmds;
bool match = !strcmp(cmd, t->name);
sys_dbg_args("match %d %-8s -> %s", j,
t->name, match ? "yes" : "no");
if (match) {
sub = t;
break;
}
cmds++;
}
if (sub != NULL)
next++;
}
if (sub == NULL)
return next;
mpp_sthd_lock(srv->thd);
list_for_each_entry_safe(impl, tmp, &srv->list, MpiImpl, list) {
if (impl->type != MPP_CTX_ENC)
continue;
if (ctx_id >= 0 && impl->ctx_id != ctx_id)
continue;
sub->cmd(srv, &argv[next + 1], impl);
}
mpp_sthd_unlock(srv->thd);
return next;
}
static rk_s32 mpp_sys_venc_dump_cfg(MppSysSrv *srv, const char **argv, void *ctx)
{
MpiImpl *impl = (MpiImpl *)ctx;
Mpp *mpp = impl->ctx;
MppEncCfg cfg;
FILE *fp;
if (NULL == mpp->mEnc) {
mpp_loge("venc %d dump cfg but not init", impl->ctx_id);
return 0;
}
cfg = mpp_enc_to_cfg(mpp->mEnc);
if (NULL == cfg) {
mpp_loge("venc %d dump cfg but get cfg failed", impl->ctx_id);
return 0;
}
{
char file_name[128];
char *buf = NULL;
snprintf(file_name, sizeof(file_name) - 1, "%s/mpp-%d-%d.json",
argv[0], srv->pid, impl->ctx_id);
fp = fopen(file_name, "w+b");
if (NULL == fp) {
mpp_loge("venc %d dump cfg but open file %s failed %s\n",
impl->ctx_id, file_name, strerror(errno));
return 0;
}
mpp_enc_cfg_extract(cfg, MPP_CFG_STR_FMT_JSON, &buf);
if (buf) {
rk_s32 len = strlen(buf);
rk_s32 wr_nr = 0;
wr_nr = fwrite(buf, 1, len, fp);
if (wr_nr != len) {
mpp_loge("venc %d write cfg to file %s failed, written %d/%d\n",
impl->ctx_id, file_name, wr_nr, len);
}
mpp_free(buf);
}
fclose(fp);
}
return 1;
}
static rk_s32 mpp_sys_venc_show_cfg(MppSysSrv *srv, const char **argv, void *ctx)
{
MpiImpl *impl = (MpiImpl *)ctx;
Mpp *mpp = impl->ctx;
(void)srv;
(void)argv;
mpp_logi("venc %d dump cfg:", impl->ctx_id);
if (mpp->mEnc) {
MppEncCfg cfg = mpp_enc_to_cfg(mpp->mEnc);
if (cfg) {
char *buf = NULL;
mpp_enc_cfg_extract(cfg, MPP_CFG_STR_FMT_LOG, &buf);
if (buf) {
mpp_cfg_print_string(buf);
mpp_free(buf);
}
}
}
return 0;
}
static rk_s32 mpp_sys_ctrl(rk_s32 pid, char *cmd, rk_s32 n)
{
MppSysSrv *srv = get_srv_sys();
MppSysCmdInfo **cmds = NULL;
const char *argv[32];
rk_s32 argc;
rk_s32 i;
sys_dbg_cmd("mpp-%d [cmd]: (%d) %s", pid, strlen(cmd), cmd);
if (!srv || !srv->thd)
return rk_nok;
argc = split_line(cmd, n, argv, 32);
sys_dbg_args("argc %d", argc);
for (i = 0; i < argc; i++)
sys_dbg_args("argv[%d]: %s", i, argv[i]);
for (i = 0; i < argc; i++) {
rk_s32 j = 0;
if (!argv[i])
break;
sys_dbg_args("argv[%d]: %s", i, argv[i]);
cmds = &sys_cmds[0];
while (*cmds) {
MppSysCmdInfo *info = *cmds;
bool match = !strcmp(argv[i], info->name);
sys_dbg_args("match %d %-8s -> %s", j,
info->name, match ? "yes" : "no");
if (match) {
i += info->cmd(srv, &argv[i], NULL);
break;
}
cmds++;
j++;
}
}
return 0;
}
static void *mpp_sys_srv(MppSThdCtx *data)
{
MppSThd thd = data->thd;
MppSysSrv *ctx = (MppSysSrv *)data->ctx;
struct epoll_event ev = { .events = EPOLLIN };
char cmd_buf[256];
char name[128];
MppSThdStatus status;
rk_s32 nf;
rk_s32 exit_efd = ctx->exit_efd;
rk_s32 fifo_fd = -1;
rk_s32 epoll_fd = -1;
rk_s32 pid = ctx->pid;
snprintf(name, sizeof(name) - 1, "/tmp/mpp-%d-cmd", pid);
do {
if (0 != mkfifo(name, 0600)) {
sys_dbg_flow("pid %d mkfifo %s failed for %s\n",
pid, name, strerror(errno));
break;
}
fifo_fd = open(name, O_RDWR | O_CLOEXEC);
if (fifo_fd < 0) {
sys_dbg_flow("pid %d open fifo %s failed for %s\n",
pid, name, strerror(errno));
break;
}
/* add epoll and exit efd */
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd < 0) {
sys_dbg_flow("pid %d create epoll failed for %s\n",
pid, strerror(errno));
break;
}
sys_dbg_flow("pid %d create sys cmd fifo %s fd %d\n",
pid, name, fifo_fd);
/* add exit event fd */
ev.data.fd = exit_efd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, exit_efd, &ev);
ev.data.fd = fifo_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fifo_fd, &ev);
while (1) {
mpp_sthd_lock(thd);
status = mpp_sthd_get_status(thd);
mpp_sthd_unlock(thd);
if (status != MPP_STHD_RUNNING)
break;
nf = epoll_wait(epoll_fd, &ev, 1, -1);
if (nf <= 0)
continue;
// exit event
if (ev.data.fd == exit_efd) {
rk_u64 u;
rk_s32 rd = read(exit_efd, &u, 8);
if (rd == 8)
break;
}
// normal event
if (ev.data.fd == fifo_fd) {
rk_s32 n = read(fifo_fd, cmd_buf, sizeof(cmd_buf) - 1);
if (n > 0) {
cmd_buf[n] = '\0';
mpp_sys_ctrl(pid, cmd_buf, n);
}
}
}
} while (0);
/* clean up all fds */
if (epoll_fd > 0)
close(epoll_fd);
if (fifo_fd > 0) {
close(fifo_fd);
unlink(name);
}
sys_dbg_flow("pid %d sys srv finish\n", pid);
return NULL;
}
static void mpp_sys_init(void)
{
MppSysSrv *srv = srv_sys;
mpp_env_get_u32("mpp_sys_debug", &mpp_sys_debug, 0);
if (srv)
return;
srv = mpp_calloc(MppSysSrv, 1);
if (!srv) {
mpp_err_f("alloc mpp sys srv failed\n");
return;
}
srv_sys = srv;
INIT_LIST_HEAD(&srv->list);
srv->pid = getpid();
/* setup external command fifo and system service thread */
if (mpp_sys_debug != 0) {
MppSThd sys_thd = NULL;
char name[64];
rk_s32 exit_efd;
rk_s32 pid = srv->pid;
mpp_sys_debug |= SYS_DBG_EN;
/* create system service thread and its exit event fd */
exit_efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
if (exit_efd < 0) {
sys_dbg_flow("pid %d create exit event fd failed ret %d %s\n",
pid, exit_efd, strerror(errno));
return;
}
srv->exit_efd = exit_efd;
snprintf(name, sizeof(name) - 1, "mpp-%d-sys-srv", pid);
sys_thd = mpp_sthd_get(name);
if (sys_thd) {
mpp_sthd_setup(sys_thd, mpp_sys_srv, srv);
mpp_sthd_start(sys_thd);
srv->thd = sys_thd;
} else {
sys_dbg_flow("pid %d create sys srv thread failed\n", pid);
}
}
}
static void mpp_sys_deinit(void)
{
MppSysSrv *srv = srv_sys;
if (!srv)
return;
srv_sys = NULL;
if (srv->thd != NULL) {
/* notify system service to quit */
rk_u64 exit_val = 1;
rk_s32 ret;
mpp_sthd_stop(srv->thd);
ret = write(srv->exit_efd, &exit_val, sizeof(exit_val));
if (ret != sizeof(exit_val)) {
mpp_loge("write to exit_efd failed, ret %zd expected %zu\n",
ret, sizeof(exit_val));
}
/* wait for system service to quit */
mpp_sthd_stop_sync(srv->thd);
mpp_sthd_put(srv->thd);
srv->thd = NULL;
}
mpp_free(srv);
}
MPP_SINGLETON(MPP_SGLN_SYS, mpp_sys, mpp_sys_init, mpp_sys_deinit)