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

1032 lines
32 KiB
C

/* SPDX-License-Identifier: Apache-2.0 OR MIT */
/*
* Copyright (c) 2025 Rockchip Electronics Co., Ltd.
*/
#define MODULE_TAG "kmpp_meta"
#include <string.h>
#include <endian.h>
#include "rk_venc_cmd.h"
#include "mpp_env.h"
#include "mpp_mem.h"
#include "mpp_lock.h"
#include "mpp_debug.h"
#include "mpp_singleton.h"
#include "kmpp_obj.h"
#include "kmpp_meta_impl.h"
#define KMETA_DBG_FUNC (0x00000001)
#define KMETA_DBG_PTR (0x00000002)
#define KMETA_DBG_VAL (0x00000004)
#define KMETA_DBG_SIZE (0x00000008)
#define kmeta_dbg(flag, fmt, ...) mpp_dbg(kmpp_meta_debug, flag, fmt, ## __VA_ARGS__)
#define kmeta_dbg_func(fmt, ...) kmeta_dbg(KMETA_DBG_FUNC, fmt, ## __VA_ARGS__)
#define kmeta_dbg_ptr(fmt, ...) kmeta_dbg(KMETA_DBG_PTR, fmt, ## __VA_ARGS__)
#define kmeta_dbg_val(fmt, ...) kmeta_dbg(KMETA_DBG_VAL, fmt, ## __VA_ARGS__)
#define kmeta_dbg_size(fmt, ...) kmeta_dbg(KMETA_DBG_SIZE, fmt, ## __VA_ARGS__)
#define META_ON_OPS (0x00010000)
#define META_VAL_INVALID (0x00000000)
#define META_VAL_VALID (0x00000001)
#define META_VAL_READY (0x00000002)
#define META_READY_MASK (META_VAL_VALID | META_VAL_READY)
/* property mask */
#define META_VAL_IS_OBJ (0x00000010)
#define META_VAL_IS_SHM (0x00000020)
#define META_VAL_FLEX (0x00000040)
#define META_VAL_FIXED (0x00000080)
#define META_VAL_FLEX_ANY (META_VAL_FLEX | META_VAL_FIXED)
#define META_PROP_MASK (META_VAL_IS_OBJ | META_VAL_IS_SHM)
#define META_UNMASK_PROP(x) MPP_FETCH_AND(x, (~META_PROP_MASK))
#define META_FLEX_MAX_LEN (16 * 1024 * 1024)
#define META_KEY_TO_U64(key, type) ((rk_u64)((rk_u32)htobe32(key)) | ((rk_u64)type << 32))
typedef enum KmppMetaDataType_e {
/* kmpp meta data of normal data type */
TYPE_VAL_32 = '3',
TYPE_VAL_64 = '6',
TYPE_KPTR = 'k', /* kernel pointer */
TYPE_UPTR = 'u', /* userspace pointer */
TYPE_SPTR = 's', /* share memory pointer */
} KmppMetaType;
typedef struct KmppMetaSrv_s {
pthread_mutex_t lock;
struct list_head list;
KmppObjDef def;
rk_s32 offset_size;
rk_s32 offset_kmeta_id;
rk_u32 meta_id;
rk_s32 meta_count;
/* capability cache: probed at init from the kernel-synced objdef trie.
* NULL entry / cmd < 0 means the running kernel lacks that feature
* (version compatibility) — variable-length flex input is then refused
* when it exceeds the default flex capacity instead of being resized. */
KmppEntry *entry_flex_size; /* real flex capacity upper bound */
KmppEntry *entry_resize_size; /* external resize target value */
rk_s32 cmd_resize; /* resize ioctl cmd, -1 = N/A */
/* USER_DATA / USER_DATAS inline flex entries, cached at init so
* meta_flex_at skips the per-call objdef trie lookup. */
KmppEntry *entry_user_data;
KmppEntry *entry_user_datas;
/* FIX section end, probed on first get */
rk_s32 flex_fixed_size;
} KmppMetaSrv;
typedef struct KmppMetaPriv_s {
struct list_head list;
KmppObj meta;
rk_u32 meta_id;
rk_u32 kmeta_id;
} KmppMetaPriv;
static KmppMetaSrv *srv_meta = NULL;
static rk_u32 kmpp_meta_debug = 0;
#define get_meta_srv(caller) \
({ \
KmppMetaSrv *__tmp; \
if (srv_meta) { \
__tmp = srv_meta; \
} else { \
mpp_loge_f("kmpp meta srv not init at %s : %s\n", __FUNCTION__, caller); \
__tmp = NULL; \
} \
__tmp; \
})
/* FIX section end (cached after first probe). 0 = not probed. */
static rk_s32 meta_flex_fixed_size(KmppMetaSrv *srv, void *entry)
{
if (srv && entry && !srv->flex_fixed_size) {
KmppMetaFlex *fud = srv->entry_user_data ?
(KmppMetaFlex *)((rk_u8 *)entry + srv->entry_user_data->tbl.elem_offset) : NULL;
KmppMetaFlex *fuds = srv->entry_user_datas ?
(KmppMetaFlex *)((rk_u8 *)entry + srv->entry_user_datas->tbl.elem_offset) : NULL;
rk_s32 n = fud ? fud->offset : 0;
if (fuds && fuds->offset > 0 && (n == 0 || fuds->offset < n))
n = fuds->offset;
srv->flex_fixed_size = n;
}
return srv ? srv->flex_fixed_size : 0;
}
static rk_s32 kmpp_meta_impl_init(void *entry, KmppObj obj, const char *caller)
{
KmppMetaPriv *priv = (KmppMetaPriv *)kmpp_obj_to_priv(obj);
KmppMetaSrv *srv = get_meta_srv(caller);
if (srv) {
priv->meta = obj;
INIT_LIST_HEAD(&priv->list);
pthread_mutex_lock(&srv->lock);
list_add_tail(&priv->list, &srv->list);
priv->meta_id = srv->meta_id++;
srv->meta_count++;
pthread_mutex_unlock(&srv->lock);
/* kmeta_id is read from entry (kernel-set share uid) for cross-boundary
* correlation; priv->meta_id is the independent userspace counter. */
if (srv->offset_kmeta_id)
priv->kmeta_id = *(rk_u32 *)((rk_u8 *)entry + srv->offset_kmeta_id);
meta_flex_fixed_size(srv, entry);
}
return rk_ok;
}
static rk_s32 kmpp_meta_impl_deinit(void *entry, KmppObj obj, const char *caller)
{
KmppMetaPriv *priv = (KmppMetaPriv *)kmpp_obj_to_priv(obj);
KmppMetaSrv *srv = get_meta_srv(caller);
(void)entry;
if (srv) {
pthread_mutex_lock(&srv->lock);
list_del_init(&priv->list);
srv->meta_count--;
pthread_mutex_unlock(&srv->lock);
}
return rk_ok;
}
static void kmpp_meta_deinit(void)
{
KmppMetaSrv *srv = srv_meta;
if (!srv) {
kmeta_dbg_func("kmpp meta already deinit\n");
return;
}
if (srv->def) {
kmpp_objdef_put(srv->def);
srv->def = NULL;
}
pthread_mutex_destroy(&srv->lock);
MPP_FREE(srv);
srv_meta = NULL;
}
static void kmpp_meta_init(void)
{
KmppMetaSrv *srv = srv_meta;
pthread_mutexattr_t attr;
mpp_env_get_u32("kmpp_meta_debug", &kmpp_meta_debug, 0);
if (srv) {
kmeta_dbg_func("kmpp meta %p already init\n", srv);
kmpp_meta_deinit();
}
srv = mpp_calloc(KmppMetaSrv, 1);
if (!srv) {
mpp_loge_f("kmpp meta malloc failed\n");
return;
}
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&srv->lock, &attr);
pthread_mutexattr_destroy(&attr);
INIT_LIST_HEAD(&srv->list);
srv->meta_id = 1;
kmpp_objdef_get(&srv->def, sizeof(KmppMetaPriv), "KmppMeta",
KMPP_OBJDEF_MISMATCH_LOG_DISABLE);
if (!srv->def) {
kmeta_dbg_func("kmpp meta get objdef failed\n");
MPP_FREE(srv);
return;
}
kmpp_objdef_add_init(srv->def, kmpp_meta_impl_init);
kmpp_objdef_add_deinit(srv->def, kmpp_meta_impl_deinit);
{
KmppEntry *tbl = NULL;
kmpp_objdef_get_entry(srv->def, "size", &tbl);
srv->offset_size = tbl ? tbl->tbl.elem_offset : 0;
kmpp_objdef_get_entry(srv->def, "kmeta_id", &tbl);
srv->offset_kmeta_id = tbl ? tbl->tbl.elem_offset : 0;
}
/*
* Probe capability cache for version-compatible flex resize: cache the
* flex_size / resize_size entry pointers and the resize ioctl cmd so
* set_ptr can both skip trie lookups and detect old kernels (NULL entry
* or cmd < 0) that lack the resize path.
*/
kmpp_objdef_get_entry(srv->def, "flex_size", &srv->entry_flex_size);
kmpp_objdef_get_entry(srv->def, "resize_size", &srv->entry_resize_size);
srv->cmd_resize = kmpp_objdef_get_cmd(srv->def, "resize");
{
rk_u64 name_ud = META_KEY_TO_U64(KEY_USER_DATA, TYPE_UPTR);
rk_u64 name_uds = META_KEY_TO_U64(KEY_USER_DATAS, TYPE_UPTR);
kmpp_objdef_get_entry(srv->def, (const char *)&name_ud, &srv->entry_user_data);
kmpp_objdef_get_entry(srv->def, (const char *)&name_uds, &srv->entry_user_datas);
}
srv_meta = srv;
}
MPP_SINGLETON(MPP_SGLN_KMPP_META, kmpp_meta, kmpp_meta_init, kmpp_meta_deinit)
static void *meta_key_to_addr(KmppObj meta, KmppMetaKey key, KmppMetaType type)
{
if (meta) {
KmppMetaSrv *srv = srv_meta;
rk_u64 val = META_KEY_TO_U64(key, type);
KmppEntry *tbl = NULL;
kmpp_objdef_get_entry(srv->def, (const char *)&val, &tbl);
if (tbl)
return ((rk_u8 *)kmpp_obj_to_entry(meta)) + tbl->tbl.elem_offset;
}
return NULL;
}
static rk_s32 meta_inc_size(KmppObj meta, rk_s32 val, const char *caller)
{
rk_s32 ret = 0;
if (meta && srv_meta) {
void *entry = kmpp_obj_to_entry(meta);
rk_s32 offset = srv_meta->offset_size;
if (entry && offset) {
rk_s32 *p = (rk_s32 *)((rk_u8 *)entry + offset);
ret = MPP_FETCH_ADD(p, val);
kmeta_dbg_size("meta %p size %d -> %d at %s\n",
meta, p[0], ret, caller);
}
}
return ret;
}
static rk_s32 meta_dec_size(KmppObj meta, rk_s32 val, const char *caller)
{
rk_s32 ret = 0;
if (meta && srv_meta) {
void *entry = kmpp_obj_to_entry(meta);
rk_s32 offset = srv_meta->offset_size;
if (entry && offset) {
rk_s32 *p = (rk_s32 *)((rk_u8 *)entry + offset);
ret = MPP_FETCH_SUB(p, val);
kmeta_dbg_size("meta %p size %d -> %d at %s\n",
meta, p[0], ret, caller);
}
}
return ret;
}
static rk_s32 meta_get_size(KmppObj meta)
{
void *entry = kmpp_obj_to_entry(meta);
rk_s32 offset = srv_meta ? srv_meta->offset_size : 0;
return (entry && offset) ? *(rk_s32 *)((rk_u8 *)entry + offset) : -1;
}
static rk_u32 meta_get_id(KmppObj meta)
{
KmppMetaPriv *priv = (KmppMetaPriv *)kmpp_obj_to_priv(meta);
return priv ? priv->kmeta_id : 0;
}
rk_s32 kmpp_meta_get(KmppMeta *meta, const char *caller)
{
KmppMetaSrv *srv = get_meta_srv(caller);
if (!srv)
return rk_nok;
return kmpp_obj_get(meta, srv->def, caller);
}
rk_s32 kmpp_meta_put(KmppMeta meta, const char *caller)
{
KmppMetaSrv *srv = get_meta_srv(caller);
if (!srv)
return rk_nok;
return kmpp_obj_put(meta, caller);
}
rk_s32 kmpp_meta_size(KmppMeta meta, const char *caller)
{
return meta_inc_size(meta, 0, caller);
}
rk_s32 kmpp_meta_dump(KmppMeta meta, const char *caller)
{
return kmpp_obj_udump_f(meta, caller);
}
rk_s32 kmpp_meta_dump_all(const char *caller)
{
KmppMetaSrv *srv = get_meta_srv(caller);
if (srv) {
KmppMeta meta = NULL;
KmppMetaPriv *pos, *n;
pthread_mutex_lock(&srv->lock);
list_for_each_entry_safe(pos, n, &srv->list, KmppMetaPriv, list) {
meta = pos->meta;
mpp_logi("meta %p:%d size %d\n", meta, pos->meta_id,
kmpp_meta_size(meta, caller));
kmpp_meta_dump(meta, caller);
}
}
return rk_ok;
}
#define KMPP_META_ACCESSOR(func_type, arg_type, key_type, key_field) \
rk_s32 kmpp_meta_set_##func_type(KmppMeta meta, KmppMetaKey key, arg_type val) \
{ \
KmppMetaVal *meta_val = meta_key_to_addr(meta, key, key_type); \
if (!meta_val) \
return rk_nok; \
if (MPP_BOOL_CAS(&meta_val->state, META_VAL_INVALID, META_VAL_VALID)) \
meta_inc_size(meta, 1, __FUNCTION__); \
meta_val->key_field = val; \
MPP_FETCH_OR(&meta_val->state, META_VAL_READY); \
return rk_ok; \
} \
rk_s32 kmpp_meta_get_##func_type(KmppMeta meta, KmppMetaKey key, arg_type *val) \
{ \
KmppMetaVal *meta_val = meta_key_to_addr(meta, key, key_type); \
if (!meta_val) \
return rk_nok; \
if (MPP_BOOL_CAS(&meta_val->state, META_READY_MASK, META_VAL_INVALID)) { \
if (val) *val = meta_val->key_field; \
meta_dec_size(meta, 1, __FUNCTION__); \
return rk_ok; \
} \
return rk_nok; \
} \
rk_s32 kmpp_meta_get_##func_type##_d(KmppMeta meta, KmppMetaKey key, arg_type *val, arg_type def) \
{ \
KmppMetaVal *meta_val = meta_key_to_addr(meta, key, key_type); \
if (!meta_val) \
return rk_nok; \
if (MPP_BOOL_CAS(&meta_val->state, META_READY_MASK, META_VAL_INVALID)) { \
if (val) *val = meta_val->key_field; \
meta_dec_size(meta, 1, __FUNCTION__); \
} else { \
if (val) *val = def; \
} \
return rk_ok; \
}
KMPP_META_ACCESSOR(s32, rk_s32, TYPE_VAL_32, val_s32)
KMPP_META_ACCESSOR(s64, rk_s64, TYPE_VAL_64, val_s64)
rk_s32 kmpp_meta_set_obj(KmppMeta meta, KmppMetaKey key, KmppObj val)
{
KmppMetaObj *meta_obj = meta_key_to_addr(meta, key, TYPE_SPTR);
if (!meta_obj)
return rk_nok;
if (MPP_BOOL_CAS(&meta_obj->state, META_VAL_INVALID, META_VAL_VALID))
meta_inc_size(meta, 1, __FUNCTION__);
{
KmppShmPtr *ptr = kmpp_obj_to_shm(val);
if (ptr) {
meta_obj->val_shm.uaddr = ptr->uaddr;
meta_obj->val_shm.kaddr = ptr->kaddr;;
MPP_FETCH_OR(&meta_obj->state, META_VAL_IS_SHM);
} else {
meta_obj->val_shm.uaddr = 0;
meta_obj->val_shm.kptr = val;
MPP_FETCH_AND(&meta_obj->state, ~META_VAL_IS_SHM);
}
}
MPP_FETCH_OR(&meta_obj->state, META_VAL_READY);
return rk_ok;
}
rk_s32 kmpp_meta_get_obj(KmppMeta meta, KmppMetaKey key, KmppObj *val)
{
KmppMetaObj *meta_obj = meta_key_to_addr(meta, key, TYPE_SPTR);
if (!meta_obj)
return rk_nok;
META_UNMASK_PROP(&meta_obj->state);
if (MPP_BOOL_CAS(&meta_obj->state, META_READY_MASK, META_VAL_INVALID)) {
if (val) {
KmppShmPtr sptr = meta_obj->val_shm;
kmpp_obj_get_by_sptr_f(val, &sptr);
}
meta_dec_size(meta, 1, __FUNCTION__);
return rk_ok;
}
return rk_nok;
}
rk_s32 kmpp_meta_get_obj_d(KmppMeta meta, KmppMetaKey key, KmppObj *val, KmppObj def)
{
KmppMetaObj *meta_obj = meta_key_to_addr(meta, key, TYPE_SPTR);
if (!meta_obj)
return rk_nok;
META_UNMASK_PROP(&meta_obj->state);
if (MPP_BOOL_CAS(&meta_obj->state, META_READY_MASK, META_VAL_INVALID)) {
if (val)
*val = meta_obj->val_shm.kptr;
meta_dec_size(meta, 1, __FUNCTION__);
} else {
if (val)
*val = def ? def : NULL;
}
return rk_ok;
}
rk_s32 kmpp_meta_set_shm(KmppMeta meta, KmppMetaKey key, KmppShmPtr *sptr)
{
KmppMetaObj *meta_obj = (KmppMetaObj *)meta_key_to_addr(meta, key, TYPE_SPTR);
if (!meta_obj)
return rk_nok;
if (MPP_BOOL_CAS(&meta_obj->state, META_VAL_INVALID, META_VAL_VALID))
meta_inc_size(meta, 1, __FUNCTION__);
if (sptr) {
meta_obj->val_shm.uaddr = sptr->uaddr;
meta_obj->val_shm.kaddr = sptr->kaddr;
} else {
meta_obj->val_shm.uaddr = 0;
meta_obj->val_shm.kptr = 0;
}
if (sptr && sptr->uaddr)
MPP_FETCH_OR(&meta_obj->state, META_VAL_IS_SHM);
else
MPP_FETCH_AND(&meta_obj->state, ~META_VAL_IS_SHM);
MPP_FETCH_OR(&meta_obj->state, META_VAL_READY);
return rk_ok;
}
rk_s32 kmpp_meta_get_shm(KmppMeta meta, KmppMetaKey key, KmppShmPtr *sptr)
{
KmppMetaObj *meta_obj = meta_key_to_addr(meta, key, TYPE_SPTR);
if (!meta_obj)
return rk_nok;
META_UNMASK_PROP(&meta_obj->state);
if (MPP_BOOL_CAS(&meta_obj->state, META_READY_MASK, META_VAL_INVALID)) {
if (sptr) {
sptr->uaddr = meta_obj->val_shm.uaddr;
sptr->kaddr = meta_obj->val_shm.kaddr;
}
meta_dec_size(meta, 1, __FUNCTION__);
return rk_ok;
}
return rk_nok;
}
rk_s32 kmpp_meta_get_shm_d(KmppMeta meta, KmppMetaKey key, KmppShmPtr *sptr, KmppShmPtr *def)
{
KmppMetaObj *meta_obj = meta_key_to_addr(meta, key, TYPE_SPTR);
if (!meta_obj)
return rk_nok;
META_UNMASK_PROP(&meta_obj->state);
if (MPP_BOOL_CAS(&meta_obj->state, META_READY_MASK, META_VAL_INVALID)) {
if (sptr) {
sptr->uaddr = meta_obj->val_shm.uaddr;
sptr->kaddr = meta_obj->val_shm.kaddr;
}
meta_dec_size(meta, 1, __FUNCTION__);
} else {
if (sptr) {
if (def) {
sptr->uaddr = def->uaddr;
sptr->kaddr = def->kaddr;
} else {
sptr->uaddr = 0;
sptr->kaddr = 0;
}
}
}
return rk_ok;
}
/*
* Flex inline set_ptr / get_ptr (mirror kernel kmpp/objs/kmpp_meta.c).
*
* Fixed flex keys (ROI / JPEG_ROI / OSD_DATA3) are memcpy'd into a fixed slot.
* Variable-length keys (USER_DATA / USER_DATAS) serialize MppEncUserData(Set)
* into the flex data area, resizing the shm entry when it does not fit.
*
* flex_base = entry + entry_size + flags_size; KmppMetaFlex.offset is relative
* to flex_base. The meta object is shared shm, so this layout must match the
* kernel exactly.
*
* NOTE: serialized KmppShmPtr.kptr fields point into the flex data area. For
* len>0 data the pointer stored is the userspace address; the user/kernel
* address mapping is finalized separately (stage 2). len==0 / count==0 keep
* kptr NULL, which is enough for the meta round-trip test.
*/
/* locate a variable-length flex entry (USER_DATA / USER_DATAS) via the entry
* pointers cached in srv at init. Returns KmppMetaFlex* at entry + elem_offset. */
static KmppMetaFlex *meta_flex_at(void *entry, KmppMetaKey key)
{
KmppMetaSrv *srv = srv_meta;
KmppEntry *tbl;
if (!srv)
return NULL;
if (key == KEY_USER_DATA)
tbl = srv->entry_user_data;
else if (key == KEY_USER_DATAS)
tbl = srv->entry_user_datas;
else
return NULL;
return tbl ? (KmppMetaFlex *)((rk_u8 *)entry + tbl->tbl.elem_offset) : NULL;
}
/* serialized length of a variable-length flex value (USER_DATA / USER_DATAS)
* inlined into the flex area; returns -1 if it exceeds META_FLEX_MAX_LEN. */
static rk_s32 meta_flex_data_len(void *val, rk_s32 is_usr_datas)
{
if (is_usr_datas) {
MppEncUserDataSetShm *uds = (MppEncUserDataSetShm *)val;
rk_s64 sum = (rk_s64)sizeof(MppEncUserDataSetShm) +
(rk_s64)sizeof(MppEncUserDataFullShm) * uds->count;
rk_s32 i;
for (i = 0; i < (rk_s32)uds->count; i++) {
MppEncUserDataFullShm *e = &uds->data[i];
if (e->uuid.uptr)
sum += MPP_ENC_USER_DATA_UUID_LEN;
sum += e->len;
}
if (sum > META_FLEX_MAX_LEN) {
mpp_loge_f("usr_datas too large %lld count %u\n", sum, uds->count);
return -1;
}
return (rk_s32)sum;
} else {
MppEncUserDataShm *ud = (MppEncUserDataShm *)val;
rk_s64 sum = (rk_s64)sizeof(MppEncUserDataShm) + ud->len;
if (sum > META_FLEX_MAX_LEN) {
mpp_loge_f("usr_data too large %lld len %u\n", sum, ud->len);
return -1;
}
return (rk_s32)sum;
}
}
/* fixed-size inline flex (ROI / JPEG_ROI / OSD3): straight memcpy */
static void meta_flex_set_fixed(rk_u8 *flex_base, KmppMetaFlex *flex, void *val)
{
memcpy(flex_base + flex->offset, val, flex->length);
}
/* variable-length flex (USER_DATA / USER_DATAS): grow the shm flex area when
* needed, shift later data to make room, then serialize header + payload. */
static rk_s32 meta_flex_set_var(KmppMeta meta, void *entry, KmppEntry *tbl,
void *val, KmppMetaKey key)
{
KmppMetaSrv *srv = srv_meta;
rk_s32 is_usr_datas = (key == KEY_USER_DATAS);
rk_s32 data_len = meta_flex_data_len(val, is_usr_datas);
KmppMetaFlex *flex = (KmppMetaFlex *)((rk_u8 *)entry + tbl->tbl.elem_offset);
rk_u8 *flex_base = (rk_u8 *)kmpp_obj_to_entry_flex(meta);
KmppMetaFlex *flex_ud;
KmppMetaFlex *flex_uds;
rk_s32 ud_len;
rk_s32 uds_len;
rk_s32 fixed_size;
rk_s32 flex_off;
if (data_len < 0)
return rk_nok;
flex_ud = meta_flex_at(entry, KEY_USER_DATA);
flex_uds = meta_flex_at(entry, KEY_USER_DATAS);
ud_len = flex_ud ? flex_ud->length : 0;
uds_len = flex_uds ? flex_uds->length : 0;
/* variable entries sit after the FIXED region; USER_DATA's offset is the
* kernel-set boundary — read it instead of hardcoding struct sizes, which
* differ between user/kernel (e.g. MppEncROICfg). */
fixed_size = flex_ud ? flex_ud->offset : 0;
flex_off = fixed_size + (is_usr_datas ? ud_len : 0);
if (flex->length == 0)
flex->offset = flex_off;
/*
* Grow the shm flex area when the new total exceeds the real capacity
* (flex_size); the kernel-side kmpp_meta_set_ptr uses flex_size as the
* upper bound, and we match it here. resize_size carries the target value
* to the resize ioctl. On old kernels lacking the resize entry/cmd (cached
* at init as NULL / < 0), refuse oversized input instead of resizing.
*/
{
rk_s32 needed = fixed_size + (is_usr_datas ? ud_len : uds_len) + data_len;
rk_s32 flex_size = 0;
if (srv->entry_flex_size)
kmpp_obj_tbl_get_s32(meta, srv->entry_flex_size, &flex_size);
if (needed > flex_size) {
if (srv->cmd_resize >= 0 && srv->entry_resize_size) {
rk_s32 ret;
kmpp_obj_tbl_set_s32(meta, srv->entry_resize_size, needed);
ret = kmpp_obj_resize_f(meta, needed);
if (ret) {
mpp_loge_f("flex resize to %d failed ret %d\n", needed, ret);
return ret;
}
/* resize may have moved the entry — rebind all pointers */
entry = kmpp_obj_to_entry(meta);
flex_base = (rk_u8 *)kmpp_obj_to_entry_flex(meta);
flex = (KmppMetaFlex *)((rk_u8 *)entry + tbl->tbl.elem_offset);
flex_ud = meta_flex_at(entry, KEY_USER_DATA);
flex_uds = meta_flex_at(entry, KEY_USER_DATAS);
ud_len = flex_ud ? flex_ud->length : 0;
uds_len = flex_uds ? flex_uds->length : 0;
} else {
mpp_loge_f("flex overflow %d > capacity %d, resize unsupported on old kernel\n",
needed, flex_size);
return rk_nok;
}
}
}
/* make room: when USER_DATA grows (incl. first set), shift the trailing
* USER_DATAS data so USER_DATA doesn't overwrite it. USER_DATAS is always
* the last entry, so writing it needs no shift. */
if (!is_usr_datas && uds_len > 0) {
rk_s32 delta = data_len - flex->length;
if (delta != 0) {
rk_u8 *src = flex_base + flex_uds->offset;
memmove(src + delta, src, uds_len);
flex_uds->offset += delta;
}
}
if (data_len > flex->capacity)
flex->capacity = data_len;
/* write serialized header + data */
{
rk_u8 *dst = flex_base + flex->offset;
if (is_usr_datas) {
MppEncUserDataSetShm *uds = (MppEncUserDataSetShm *)val;
MppEncUserDataSetShm *set_hdr = (MppEncUserDataSetShm *)dst;
MppEncUserDataFullShm *entries = set_hdr->data;
rk_s32 hdr_size = sizeof(MppEncUserDataSetShm) +
sizeof(MppEncUserDataFullShm) * uds->count;
rk_u8 *ptr = dst + hdr_size;
rk_s32 i;
set_hdr->count = uds->count;
for (i = 0; i < (rk_s32)uds->count; i++) {
MppEncUserDataFullShm *e = &uds->data[i];
entries[i].len = e->len;
if (e->uuid.uptr) {
entries[i].uuid.uaddr = (rk_u64)(uintptr_t)ptr;
memcpy(ptr, e->uuid.uptr, MPP_ENC_USER_DATA_UUID_LEN);
ptr += MPP_ENC_USER_DATA_UUID_LEN;
} else {
entries[i].uuid.uaddr = 0;
}
if (e->data.uptr && e->len > 0) {
entries[i].data.uaddr = (rk_u64)(uintptr_t)ptr;
memcpy(ptr, e->data.uptr, e->len);
ptr += e->len;
} else {
entries[i].data.uaddr = 0;
}
}
} else {
MppEncUserDataShm *ud = (MppEncUserDataShm *)val;
MppEncUserDataShm *hdr = (MppEncUserDataShm *)dst;
hdr->len = ud->len;
if (ud->data.uptr && ud->len > 0) {
/* uaddr not uptr: void* only fills low 4 bytes on 32-bit */
hdr->data.uaddr = (rk_u64)(uintptr_t)(dst + sizeof(MppEncUserDataShm));
memcpy(hdr->data.uptr, ud->data.uptr, ud->len);
} else {
hdr->data.uaddr = 0;
}
}
}
flex->length = data_len;
return rk_ok;
}
rk_s32 kmpp_meta_set_ptr(KmppMeta meta, KmppMetaKey key, void *val)
{
void *entry = kmpp_obj_to_entry(meta);
KmppMetaSrv *srv = srv_meta;
rk_u64 name = META_KEY_TO_U64(key, TYPE_UPTR);
KmppEntry *tbl = NULL;
rk_u32 *state;
rk_s32 old_count = meta_get_size(meta);
if (!srv)
return rk_nok;
kmpp_objdef_get_entry(srv->def, (const char *)&name, &tbl);
if (!tbl)
return rk_nok;
state = (rk_u32 *)((rk_u8 *)entry + tbl->tbl.elem_offset);
if (*state & META_VAL_FLEX_ANY) {
KmppMetaFlex *flex = (KmppMetaFlex *)state;
if (!val) {
/* NULL clears the entry (mirror mpp set_user_data NULL = clean) */
flex->length = 0;
} else if (*state & META_VAL_FIXED) {
/* fixed-size inline */
rk_u8 *flex_base;
rk_s32 flex_size = 0;
rk_s32 needed = meta_flex_fixed_size(srv, entry);
if (needed > 0) {
if (srv->entry_flex_size)
kmpp_obj_tbl_get_s32(meta, srv->entry_flex_size, &flex_size);
if (needed > flex_size) {
rk_s32 ret;
if (srv->cmd_resize < 0 || !srv->entry_resize_size) {
mpp_loge_f("FIXED flex %d > capacity %d, resize unsupported on old kernel\n",
needed, flex_size);
return rk_nok;
}
kmpp_obj_tbl_set_s32(meta, srv->entry_resize_size, needed);
ret = kmpp_obj_resize_f(meta, needed);
if (ret) {
mpp_loge_f("FIXED flex resize to %d failed ret %d\n", needed, ret);
return ret;
}
entry = kmpp_obj_to_entry(meta);
flex = (KmppMetaFlex *)((rk_u8 *)entry + tbl->tbl.elem_offset);
state = (rk_u32 *)flex;
}
}
flex_base = (rk_u8 *)kmpp_obj_to_entry_flex(meta);
meta_flex_set_fixed(flex_base, flex, val);
} else {
/* variable-length inline (USER_DATA / USER_DATAS) */
rk_s32 ret = meta_flex_set_var(meta, entry, tbl, val, key);
if (ret)
return ret;
/* meta_flex_set_var may have resized (moved the shm entry) — rebind
* flex and state (flex's first u32) before the state update below,
* else they point at the freed shm. */
flex = (KmppMetaFlex *)((rk_u8 *)kmpp_obj_to_entry(meta) + tbl->tbl.elem_offset);
state = (rk_u32 *)flex;
}
{
rk_u32 old = *state;
if ((old & META_READY_MASK) == 0 &&
MPP_BOOL_CAS(state, old, old | META_VAL_VALID))
meta_inc_size(meta, 1, __FUNCTION__);
}
} else {
/* LOC_TBL: plain pointer storage */
KmppMetaVal *meta_val = (KmppMetaVal *)state;
if (MPP_BOOL_CAS(state, META_VAL_INVALID, META_VAL_VALID))
meta_inc_size(meta, 1, __FUNCTION__);
meta_val->val_ptr = val;
}
/* state is the first u32 of both KmppMetaFlex and KmppMetaVal, so the
* READY flag is set uniformly without casting back to either struct. */
MPP_FETCH_OR(state, META_VAL_READY);
kmeta_dbg_ptr("meta %d set ptr %s state %x val %px count %d -> %d\n",
meta_get_id(meta), (const char *)&name, *state, val,
old_count, meta_get_size(meta));
return rk_ok;
}
/* consume a ready ptr/flex entry: CAS ready→consumed, fill *val, dec_size.
* returns rk_ok on success, rk_nok if not ready or CAS lost. */
static rk_s32 meta_get_ptr_internal(KmppMeta meta, rk_u32 *state,
void **val, rk_u64 name)
{
rk_s32 ret = rk_nok;
rk_s32 old_count = meta_get_size(meta);
if (*state & META_VAL_FLEX_ANY) {
KmppMetaFlex *flex = (KmppMetaFlex *)state;
rk_u32 old = flex->state;
if ((old & META_READY_MASK) == META_READY_MASK &&
MPP_BOOL_CAS(&flex->state, old, old & ~META_READY_MASK)) {
if (val) {
rk_u8 *flex_base = (rk_u8 *)kmpp_obj_to_entry_flex(meta);
*val = flex_base + flex->offset;
}
meta_dec_size(meta, 1, __FUNCTION__);
ret = rk_ok;
}
} else {
KmppMetaVal *meta_val = (KmppMetaVal *)state;
rk_u32 old = meta_val->state;
if ((old & META_READY_MASK) == META_READY_MASK &&
MPP_BOOL_CAS(&meta_val->state, old, META_VAL_INVALID)) {
if (val)
*val = meta_val->val_ptr;
meta_dec_size(meta, 1, __FUNCTION__);
ret = rk_ok;
}
}
kmeta_dbg_ptr("meta %d get ptr %s state %x ret %d val %px count %d -> %d\n",
meta_get_id(meta), (const char *)&name, *state, ret, val ? *val : NULL,
old_count, meta_get_size(meta));
return ret;
}
rk_s32 kmpp_meta_get_ptr(KmppMeta meta, KmppMetaKey key, void **val)
{
void *entry = kmpp_obj_to_entry(meta);
KmppMetaSrv *srv = srv_meta;
rk_u64 name = META_KEY_TO_U64(key, TYPE_UPTR);
KmppEntry *tbl = NULL;
rk_u32 *state;
if (!srv)
return rk_nok;
kmpp_objdef_get_entry(srv->def, (const char *)&name, &tbl);
if (!tbl)
return rk_nok;
state = (rk_u32 *)((rk_u8 *)entry + tbl->tbl.elem_offset);
return meta_get_ptr_internal(meta, state, val, name);
}
rk_s32 kmpp_meta_get_ptr_d(KmppMeta meta, KmppMetaKey key, void **val, void *def)
{
void *entry = kmpp_obj_to_entry(meta);
KmppMetaSrv *srv = srv_meta;
rk_u64 name = META_KEY_TO_U64(key, TYPE_UPTR);
KmppEntry *tbl = NULL;
rk_u32 *state;
rk_s32 ret;
if (!srv)
return rk_nok;
kmpp_objdef_get_entry(srv->def, (const char *)&name, &tbl);
if (!tbl)
return rk_nok;
state = (rk_u32 *)((rk_u8 *)entry + tbl->tbl.elem_offset);
ret = meta_get_ptr_internal(meta, state, val, name);
if (ret && val)
*val = def;
return rk_ok;
}
/* Peek a ready ptr/flex entry without consuming it (no CAS, no dec_size). */
rk_s32 kmpp_meta_peek_ptr(KmppMeta meta, KmppMetaKey key, void **val)
{
void *entry = kmpp_obj_to_entry(meta);
KmppMetaSrv *srv = srv_meta;
rk_u64 name = META_KEY_TO_U64(key, TYPE_UPTR);
KmppEntry *tbl = NULL;
rk_u32 *state;
if (!srv || !val)
return rk_nok;
*val = NULL;
kmpp_objdef_get_entry(srv->def, (const char *)&name, &tbl);
if (!tbl)
return rk_nok;
state = (rk_u32 *)((rk_u8 *)entry + tbl->tbl.elem_offset);
if ((*state & META_READY_MASK) != META_READY_MASK)
return rk_nok;
if (*state & META_VAL_FLEX_ANY) {
KmppMetaFlex *flex = (KmppMetaFlex *)state;
rk_u8 *flex_base = (rk_u8 *)kmpp_obj_to_entry_flex(meta);
*val = flex_base + flex->offset;
} else {
KmppMetaVal *meta_val = (KmppMetaVal *)state;
*val = meta_val->val_ptr;
}
return rk_ok;
}
/* OSD wrapper: set_osd writes OSD_DATA4; get_osd peeks OSD_DATA4 then OSD_DATA3. */
rk_s32 kmpp_meta_set_osd(KmppMeta meta, MppEncOSDData3 *osd)
{
return kmpp_meta_set_ptr(meta, KEY_OSD_DATA4, osd);
}
rk_s32 kmpp_meta_get_osd(KmppMeta meta, MppEncOSDData3 **osd)
{
rk_s32 ret;
if (!osd)
return rk_nok;
*osd = NULL;
ret = kmpp_meta_peek_ptr(meta, KEY_OSD_DATA4, (void **)osd);
if (ret || !*osd)
ret = kmpp_meta_peek_ptr(meta, KEY_OSD_DATA3, (void **)osd);
return ret;
}