| /* |
| * drivers/amlogic/media/common/codec_mm/configs/configs.c |
| * |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| * more details. |
| * |
| */ |
| |
| #include <linux/amlogic/media/codec_mm/configs.h> |
| #include "configs_priv.h" |
| static struct mconfig_node root_node; |
| static struct mconfig_node *configs_get_root_node(void) |
| { |
| return &root_node; |
| } |
| |
| #ifdef DEBUG_CONFIG |
| #define config_debug(args...) pr_info(args) |
| #else |
| #define config_debug(args...) |
| #endif |
| int configs_inc_node_ref( |
| struct mconfig_node *node) |
| { |
| return atomic_inc_return(&node->ref_cnt); |
| } |
| |
| int configs_dec_node_ref( |
| struct mconfig_node *node) |
| { |
| return atomic_dec_return(&node->ref_cnt); |
| } |
| |
| static int configs_put_node(struct mconfig_node *node, |
| int del_parent_ref, struct mconfig_node *root_node) |
| { |
| struct mconfig_node *pnode; |
| |
| if (!node) |
| return 0; |
| configs_dec_node_ref(node); |
| pnode = node->parent_node; |
| if (!del_parent_ref) |
| return 0; |
| while ((pnode != NULL) && (pnode != root_node)) { |
| configs_dec_node_ref(pnode); |
| pnode = pnode->parent_node; |
| } |
| return 0; |
| } |
| |
| |
| static int configs_parser_first_node( |
| const char *root_str, |
| char *sub_str, |
| int sub_size, |
| int *is_end_node) |
| { |
| int node_size; |
| char *str; |
| char *pstr; |
| |
| if (strlen(root_str) <= 0) |
| return 0; |
| str = strchr(root_str, '.'); |
| if (str != NULL) { |
| node_size = str - root_str; |
| *is_end_node = 0; |
| } else { |
| str = strchr(root_str, '='); |
| if (str != NULL) |
| node_size = str - root_str; |
| else |
| node_size = strlen(root_str); |
| *is_end_node = 1; |
| } |
| if (node_size >= MAX_ITEM_NAME - 1) |
| node_size = MAX_ITEM_NAME - 1; |
| if (node_size > 0) { |
| strncpy(sub_str, root_str, node_size); |
| sub_str[node_size] = '\0'; |
| pstr = sub_str; |
| while (pstr[0] != '\0' && |
| pstr[0] != ' ' && |
| pstr[0] != '\r' && |
| pstr[0] != '\n') { |
| /*not space. */ |
| pstr++; |
| } |
| pstr[0] = '\0'; |
| } else |
| sub_str[0] = '\0'; |
| return node_size; |
| } |
| |
| static inline const char *configs_parser_str_valstr( |
| const char *root_str) |
| { |
| char *str; |
| |
| if (strlen(root_str) <= 0) |
| return NULL; |
| str = strchr(root_str, '='); |
| if (str) |
| str = str + 1; /*del = */ |
| return str; |
| } |
| |
| static int configs_parser_value_u32(const char *str, u32 *val) |
| { |
| int ret = 0; |
| const char *pstr; |
| |
| if (!str || strlen(str) <= 0) |
| return 0; |
| pstr = str; |
| while (pstr[0] == ' ') |
| pstr++; |
| if (pstr[0] == '-') { |
| ret = sscanf(pstr, "-%d", val); |
| *val = -((int)*val); |
| } else if (strstr(pstr, "0x")) |
| ret = sscanf(pstr, "0x%x", val); |
| else if (strstr(pstr, "0x")) |
| ret = sscanf(pstr, "0x%x", val); |
| else { |
| ret = kstrtou32(pstr, 0, val); |
| if (ret == 0) |
| ret = 1; |
| } |
| return ret; |
| } |
| |
| static int configs_parser_value_u64(const char *str, u64 *val) |
| { |
| int ret = 0; |
| const char *pstr; |
| |
| if (!str || strlen(str) <= 0) |
| return 0; |
| pstr = str; |
| while (pstr[0] == ' ') |
| pstr++; |
| if (pstr[0] == '-') { |
| ret = sscanf(pstr, "-%lld", val); |
| *val = -((int)*val); |
| } else if (strstr(pstr, "0x")) |
| ret = sscanf(pstr, "0x%llx", val); |
| else if (strstr(pstr, "0x")) |
| ret = sscanf(pstr, "0x%llx", val); |
| else { |
| ret = kstrtou64(pstr, 0, val); |
| if (ret == 0) |
| ret = 1; |
| } |
| return ret; |
| } |
| |
| static int configs_parser_value_bool(const char *str, bool *pbool) |
| { |
| const char *pstr; |
| |
| if (!str || strlen(str) <= 0) |
| return 0; |
| pstr = str; |
| while (pstr[0] == ' ') |
| pstr++; |
| if (str[0] == '1') { |
| *pbool = true; |
| } else if (str[0] == '0') { |
| *pbool = false; |
| } else if (!strcmp(str, "true") || |
| !strcmp(str, "TRUE") || |
| !strcmp(str, "true")) { |
| *pbool = true; |
| } else if (strcmp(str, "false") || |
| !strcmp(str, "FALSE") || |
| !strcmp(str, "False")) { |
| *pbool = false; |
| } else { |
| return 0; |
| } |
| return 1; |
| } |
| |
| |
| static struct mconfig_node *configs_get_node_with_name( |
| struct mconfig_node |
| *rootnode, const char *node_name) |
| { |
| struct mconfig_node *snode, *need; |
| struct list_head *node_list, *next; |
| |
| need = NULL; |
| if (!rootnode) |
| rootnode = configs_get_root_node(); |
| mutex_lock(&rootnode->lock); |
| if (list_empty(&rootnode->son_node_list)) { |
| mutex_unlock(&rootnode->lock); |
| return NULL; |
| } |
| list_for_each_safe(node_list, next, &rootnode->son_node_list) { |
| snode = list_entry(node_list, struct mconfig_node, list); |
| if (!strcmp(snode->name, node_name)) { |
| need = snode; |
| if (snode->active <= 0 || |
| configs_inc_node_ref(need) < 0) |
| need = NULL; |
| break; |
| } |
| } |
| mutex_unlock(&rootnode->lock); |
| return need; |
| } |
| |
| int configs_init_new_node(struct mconfig_node *node, |
| const char *name, int rw_flags) |
| { |
| node->configs = NULL; |
| node->configs_num = 0; |
| node->son_node_num = 0; |
| node->name = name; |
| node->rw_flags = rw_flags; |
| node->depth = 0; |
| node->active = 1; |
| node->prefix[0] = '\0'; |
| atomic_set(&node->ref_cnt, 0); |
| mutex_init(&node->lock); |
| INIT_LIST_HEAD(&node->list); |
| INIT_LIST_HEAD(&node->son_node_list); |
| return 0; |
| } |
| EXPORT_SYMBOL(configs_init_new_node); |
| |
| int configs_register_node(struct mconfig_node *parent, |
| struct mconfig_node *new_node) |
| { |
| if (!parent) |
| parent = configs_get_root_node(); |
| if (!parent || new_node == parent) |
| return 0; /*top node add. */ |
| if (parent->depth >= MAX_DEPTH) |
| return -1; /*too deep path */ |
| if (strlen(parent->prefix) + strlen(parent->name) >= |
| MAX_PREFIX_NAME - 2) { |
| pr_err("unsupport deep path %s.%s,len=%d > %d\n", |
| parent->prefix, parent->name, |
| (int)(strlen(parent->prefix) + strlen(parent->name) + 1), |
| MAX_PREFIX_NAME - 1); |
| return -2; /*unsuport deep path */ |
| } |
| if (configs_get_node_with_name(parent, new_node->name) != NULL) { |
| pr_err("have register same node[%s] on %s before\n", |
| new_node->name, parent->name); |
| return -3; |
| } |
| /* |
| *root.name.bb |
| *depth:0.1.2 |
| *depth 0 &1 don't sed prefix. |
| *for ignore "root." |
| */ |
| new_node->depth = parent->depth + 1; |
| if (new_node->depth >= 2) { |
| if (parent->prefix[0]) { |
| strcpy(new_node->prefix, parent->prefix); |
| strcat(new_node->prefix, "."); |
| } |
| strcat(new_node->prefix, parent->name); |
| } |
| mutex_lock(&parent->lock); |
| new_node->parent_node = parent; |
| list_add_tail(&new_node->list, &parent->son_node_list); |
| /* |
| *node parent not have write permissions, |
| *del the the node write permissions; |
| *same as read. |
| */ |
| new_node->rw_flags = parent->rw_flags & new_node->rw_flags; |
| parent->son_node_num++; |
| new_node->active = 1; |
| mutex_unlock(&parent->lock); |
| return 0; |
| } |
| EXPORT_SYMBOL(configs_register_node); |
| |
| static struct mconfig_node *configs_get_node_path_end_node( |
| struct mconfig_node *root_node, const char *path) |
| { |
| char sub_node_name[128]; |
| struct mconfig_node *node, *start_node, *parent_node; |
| int i, end; |
| const char *next_path = path; |
| |
| if (!path) |
| return NULL; |
| end = 0; |
| start_node = root_node; |
| if (!root_node) |
| start_node = configs_get_root_node(); |
| node = start_node; |
| while (!end) { |
| parent_node = node; |
| i = configs_parser_first_node(next_path, |
| sub_node_name, 128, &end); |
| if (i <= 0 || sub_node_name[0] == '\0') { |
| pr_err("can't find [%s] 's node!!\n", path); |
| return NULL; |
| } |
| /*only no top node. */ |
| if (end && !root_node && node == start_node) { |
| if (!strcmp(node->name, sub_node_name)) { |
| /*need node is top node */ |
| break; |
| } |
| } |
| config_debug("configs_parser_first_node{%s} end%d\n", |
| sub_node_name, end); |
| node = configs_get_node_with_name(parent_node, |
| sub_node_name); |
| if (node == NULL) { |
| pr_err("can't find node:[%s], from:[%s], path=%s\n", |
| sub_node_name, parent_node->name, path); |
| return NULL; |
| } |
| next_path = next_path + i + 1; /*media.vdec --> vdec */ |
| config_debug("get snode[%s] from node[%s], end=%d\n", |
| node->name, parent_node->name, end); |
| } |
| return node; |
| } |
| EXPORT_SYMBOL(configs_get_node_path_end_node); |
| |
| int configs_register_path_node(const char *path, |
| struct mconfig_node *new_node) |
| { |
| struct mconfig_node *parent = NULL; |
| int ret; |
| |
| if (path && strlen(path) > 0) { |
| parent = configs_get_node_path_end_node(NULL, path); |
| if (!parent) |
| return -1; |
| } |
| ret = configs_register_node(parent, new_node); |
| configs_put_node(parent, 1, NULL); |
| return ret; |
| } |
| EXPORT_SYMBOL(configs_register_path_node); |
| |
| int configs_register_configs(struct mconfig_node *node, |
| struct mconfig *configs, int num) |
| { |
| if (!node) |
| return -1; |
| if (node->configs != NULL || node->configs_num > 0) { |
| pr_err("node[%s] register config before!.\n", node->name); |
| return -2; |
| } |
| if (0) { |
| int i; |
| |
| for (i = 0; i < num; i++) { |
| pr_info("init node:%s, config[i].data %lx-%lx\n", |
| node->name, |
| configs[i].ldata[0], |
| configs[i].ldata[1]); |
| } |
| } |
| mutex_lock(&node->lock); |
| node->configs = configs; |
| node->configs_num = num; |
| mutex_unlock(&node->lock); |
| return 0; |
| } |
| EXPORT_SYMBOL(configs_register_configs); |
| |
| int configs_register_path_configs(const char *path, |
| struct mconfig *configs, int num) |
| { |
| struct mconfig_node *parent = NULL; |
| int ret; |
| |
| if (path && strlen(path) > 0) { |
| parent = configs_get_node_path_end_node(NULL, path); |
| if (!parent) |
| return -1; |
| ret = configs_register_configs(parent, configs, num); |
| configs_put_node(parent, 1, NULL); |
| return ret; |
| } |
| return -1; |
| } |
| EXPORT_SYMBOL(configs_register_path_configs); |
| |
| int configs_del_endnode(struct mconfig_node *parent, |
| struct mconfig_node *node) |
| { |
| struct mconfig_node *parent_node; |
| |
| parent_node = node->parent_node; |
| node->active = 0; |
| /*always set it to no active */ |
| if (atomic_read(&node->ref_cnt) != 0) |
| return -1; |
| /*always del configs. */ |
| node->configs = NULL; |
| node->configs_num = 0; |
| /*do't del node when have son node. */ |
| if (node->son_node_num > 0) |
| return -2; |
| if (parent) { |
| mutex_lock(&parent->lock); |
| list_del(&node->list); |
| parent->son_node_num--; |
| mutex_unlock(&parent->lock); |
| } |
| return 0; |
| } |
| |
| static struct mconfig *configs_get_node_config( |
| struct mconfig_node *node, char *name) |
| { |
| int i; |
| struct mconfig *config = NULL; |
| |
| mutex_lock(&node->lock); |
| for (i = 0; i < node->configs_num; i++) { |
| struct mconfig *val = &node->configs[i]; |
| |
| if (val && !strcmp(val->item_name, name)) { |
| configs_inc_node_ref(node); |
| config = val; |
| break; |
| } |
| } |
| mutex_unlock(&node->lock); |
| config_debug("get config[%s] from %s-end-%p\n", |
| name, node->name, (void *)config); |
| return config; |
| } |
| |
| static int configs_config2str(struct mconfig *config, |
| char *buf, int size) |
| { |
| int ret = 0; |
| |
| if (size < 1) |
| return 0; |
| switch (config->type) { |
| case CONFIG_TYPE_PBOOL: |
| ret = snprintf(buf, size, "%s", |
| config->pboolval[0] ? "true" : "false"); |
| break; |
| case CONFIG_TYPE_PI32: |
| ret = snprintf(buf, size, "%u", config->pival[0]); |
| break; |
| case CONFIG_TYPE_PU32: |
| ret = snprintf(buf, size, "%d", config->pu32val[0]); |
| break; |
| case CONFIG_TYPE_PU64: |
| ret = snprintf(buf, size, "%llx", config->pu64val[0]); |
| break; |
| case CONFIG_TYPE_PSTR: |
| case CONFIG_TYPE_PCSTR: |
| ret = snprintf(buf, size, "%s", config->str); |
| break; |
| case CONFIG_TYPE_BOOL: |
| ret = snprintf(buf, size, "%s", |
| config->pboolval ? "true" : "false"); |
| break; |
| case CONFIG_TYPE_I32: |
| ret = snprintf(buf, size, "%d", config->ival); |
| break; |
| case CONFIG_TYPE_U32: |
| ret = snprintf(buf, size, "%u", config->u32val); |
| break; |
| case CONFIG_TYPE_U64: |
| ret = snprintf(buf, size, "0x%llx", config->u64val); |
| break; |
| case CONFIG_TYPE_FUN: |
| if (!config->f_get) |
| ret = 0; |
| else |
| ret = config->f_get(config->item_name, |
| config->id, buf, size); |
| break; |
| default: |
| ret = -4; |
| } |
| if (ret <= 0) { |
| pr_err("config2str error %s-type:%d,%lx-%lx,ret=%d\n", |
| config->item_name, config->type, |
| config->ldata[0], config->ldata[1], ret); |
| } else if (ret >= size) { |
| buf[size - 1] = '\0'; |
| ret = size - 1; |
| } |
| return ret; |
| } |
| |
| static int configs_str2config(struct mconfig *config, |
| const char *str) |
| { |
| int ret = 0; |
| u32 val; |
| u64 val64; |
| bool bval; |
| |
| if (!str || strlen(str) <= 0) |
| return 0; |
| switch (config->type) { |
| case CONFIG_TYPE_PBOOL: |
| case CONFIG_TYPE_BOOL: |
| ret = configs_parser_value_bool(str, &bval); |
| if (ret <= 0) |
| break; |
| if (config->type == CONFIG_TYPE_PBOOL) |
| config->pboolval[0] = bval; |
| else |
| config->boolval = bval; |
| break; |
| case CONFIG_TYPE_PI32: |
| case CONFIG_TYPE_PU32: |
| ret = configs_parser_value_u32(str, (u32 *) &val); |
| if (ret > 0) |
| config->pu32val[0] = val; |
| break; |
| case CONFIG_TYPE_PU64: |
| ret = configs_parser_value_u64(str, (u64 *) &val64); |
| if (ret > 0) |
| config->pu64val[0] = val64; |
| break; |
| case CONFIG_TYPE_PSTR: |
| strncpy(config->str, str, config->size); |
| ret = strlen(config->str); |
| break; |
| case CONFIG_TYPE_I32: |
| case CONFIG_TYPE_U32: |
| ret = configs_parser_value_u32(str, (u32 *) &val); |
| if (ret > 0) |
| config->u32val = val; |
| break; |
| case CONFIG_TYPE_U64: |
| ret = configs_parser_value_u64(str, (u64 *) &val64); |
| if (ret > 0) |
| config->u64val = val64; |
| break; |
| case CONFIG_TYPE_FUN: |
| if (!config->f_set) |
| ret = -1; |
| else |
| ret = config->f_set(config->item_name, |
| config->id, |
| str, strlen(str)); |
| break; |
| case CONFIG_TYPE_PCSTR: /*can't set. */ |
| default: |
| ret = -4; |
| } |
| return ret; |
| } |
| |
| static char *configs_build_prefix( |
| struct mconfig_node *node, |
| const char *prefix, |
| int mode, |
| char *buf, |
| int size) |
| { |
| if (!buf) |
| return ""; |
| buf[0] = '\0'; |
| if (mode & LIST_MODE_PATH_FULLPREFIX) { |
| if (node->prefix[0]) { |
| snprintf(buf, size, "%s", |
| node->prefix); |
| } |
| } else if (mode & LIST_MODE_PATH_PREFIX) { |
| if (prefix && prefix[0]) { |
| snprintf(buf, size, "%s", |
| prefix); |
| } |
| } |
| if (buf[0] && buf[strlen(buf) - 1] != '.') |
| strncat(buf, ".", size); |
| return buf; |
| } |
| |
| static int configs_list_node_configs_locked( |
| struct mconfig_node *node, |
| char *buf, int size, const char *prefix, |
| int mode) |
| { |
| int i; |
| int ret; |
| int pn_size = 0; |
| struct mconfig *config = NULL; |
| const char *cprefix = prefix; |
| |
| if (!buf || size < 8 || !node->configs) |
| return 0; |
| if (!cprefix) |
| cprefix = ""; |
| for (i = 0; i < node->configs_num && (size - pn_size) > 8; i++) { |
| config = &node->configs[i]; |
| ret = snprintf(buf + pn_size, |
| size - pn_size, |
| " %s%s.%s%s", |
| cprefix, |
| node->name, |
| config->item_name, |
| (node->rw_flags & CONFIG_FOR_R) ? "=" : ""); |
| if (ret > 0) |
| pn_size += ret; |
| if ((mode & LIST_MODE_VAL) && |
| ((mode & CONFIG_FOR_T) || |
| !(CONFIG_FOR_T & node->rw_flags)) && |
| (node->rw_flags & CONFIG_FOR_R)) { |
| ret = configs_config2str(config, |
| buf + pn_size, size - pn_size); |
| if (ret > 0) { |
| if ((pn_size + ret) < size) { |
| strcat(buf, "\n"); |
| ret++; |
| } |
| pn_size += ret; |
| } else { |
| if (ret == 0 && (pn_size + ret) < size) { |
| strcat(buf, "\n"); |
| ret++; |
| continue; |
| } |
| break; |
| } |
| } else { |
| ret = snprintf(buf + pn_size, size - pn_size, "\n"); |
| if (ret > 0) |
| pn_size += ret; |
| } |
| } |
| return pn_size; |
| } |
| |
| int configs_list_node_configs(struct mconfig_node *node, |
| char *buf, int size, |
| int mode) |
| { |
| int ret; |
| |
| mutex_lock(&node->lock); |
| ret = configs_list_node_configs_locked(node, buf, |
| size, NULL, mode); |
| mutex_unlock(&node->lock); |
| return ret; |
| } |
| |
| static int configs_list_nodes_in(struct mconfig_node *node, |
| char *buf, int size, const char *prefix, |
| int mode) |
| { |
| int ret; |
| int pn_size = 0; |
| char cprefix[MAX_PREFIX_NAME + MAX_ITEM_NAME]; |
| char *c_prefix; |
| char rw[4][4] = { "N", "r", "w", "rw" }; |
| |
| if (!node || !buf || size <= 0) |
| return 0; |
| if (!(node->rw_flags & mode)) |
| return 0;/*LIST_MODE_LIST_RD/WD flags*/ |
| config_debug("start dump node %s...\n", node->name); |
| c_prefix = configs_build_prefix(node, |
| prefix, |
| mode, |
| cprefix, |
| MAX_PREFIX_NAME + MAX_ITEM_NAME); |
| mutex_lock(&node->lock); |
| if (mode & LIST_MODE_NODE_INFO) { |
| ret = snprintf(buf + pn_size, size - pn_size, |
| "[NODE]%s%s:[%s/%d/%d/%d]\n", |
| c_prefix, |
| node->name, |
| rw[(node->rw_flags & 3)], |
| atomic_read(&node->ref_cnt), |
| node->configs_num, |
| node->depth); |
| if (ret > 0) |
| pn_size += ret; |
| } |
| if (mode & LIST_MODE_CONFIGS_VAL) { |
| ret = configs_list_node_configs_locked(node, buf + pn_size, |
| size - pn_size, c_prefix, mode); |
| if (ret > 0) |
| pn_size += ret; |
| } |
| if ((mode & LIST_MODE_SUB_NODES) && node->son_node_num > 0) { |
| struct mconfig_node *snode; |
| struct list_head *node_list, *next; |
| |
| if (mode & LIST_MODE_PATH_PREFIX) { |
| if (node->depth > 0) {/*not root.*/ |
| strncat(cprefix, node->name, |
| MAX_PREFIX_NAME + MAX_ITEM_NAME - 1); |
| } |
| } |
| list_for_each_safe(node_list, next, &node->son_node_list) { |
| snode = list_entry(node_list, |
| struct mconfig_node, list); |
| if (snode) { |
| ret = configs_list_nodes_in(snode, |
| buf + pn_size, size - pn_size, |
| cprefix, mode); |
| if (ret > 0) |
| pn_size += ret; |
| } |
| } |
| } |
| mutex_unlock(&node->lock); |
| return pn_size; |
| } |
| |
| int configs_list_nodes(struct mconfig_node *node, |
| char *buf, int size, int mode) |
| { |
| if (!node) |
| node = configs_get_root_node(); |
| return configs_list_nodes_in(node, buf, size, |
| NULL, |
| mode); |
| } |
| |
| int configs_list_path_nodes(const char *prefix, |
| char *buf, int size, int mode) |
| { |
| struct mconfig_node *node; |
| int ret = 0; |
| |
| node = configs_get_node_path_end_node(NULL, prefix); |
| if (node != NULL) { |
| ret = configs_list_nodes(node, buf, size, mode); |
| configs_put_node(node, 1, NULL); |
| } |
| return ret; |
| } |
| |
| static int configs_get_node_path_config( |
| struct mconfig_node *root_node, |
| struct mconfig **config_ret, |
| const char *path, |
| struct mconfig_node **hold_node, int set) |
| { |
| char sub_node_name[128]; |
| struct mconfig_node *node, *parent_node; |
| struct mconfig *config = NULL; |
| int i, end; |
| const char *next_path = path; |
| int err = 0; |
| |
| if (!path) |
| return -EIO; |
| end = 0; |
| if (!root_node) |
| root_node = configs_get_root_node(); |
| node = root_node; |
| while (!end) { |
| parent_node = node; |
| i = configs_parser_first_node(next_path, |
| sub_node_name, 128, &end); |
| if (i <= 0 || sub_node_name[0] == '\0') { |
| pr_err("can't find [%s] 's node!!\n", path); |
| return -1; |
| } |
| config_debug("configs_parser_first_node{%s} end%d\n", |
| sub_node_name, end); |
| if (!end) { |
| node = configs_get_node_with_name(parent_node, |
| sub_node_name); |
| if (node == NULL) { |
| if (parent_node != root_node) { |
| node = parent_node; |
| err = -EPERM; |
| goto out; |
| } |
| /*for reset refs. */ |
| pr_err("can't find node:[%s], from:[%s], path=%s\n", |
| sub_node_name, parent_node->name, path); |
| err = -ENOENT; |
| goto out; |
| } |
| next_path = next_path + i + 1; /*media.vdec --> vdec */ |
| config_debug("get snode[%s] from node[%s], end=%d\n", |
| node->name, parent_node->name, end); |
| } |
| } |
| if (set && !(node->rw_flags & CONFIG_FOR_W)) { |
| err = -ENOENT; |
| goto out; |
| } |
| if (!set && !(node->rw_flags & CONFIG_FOR_R)) { |
| err = -EPERM; |
| goto out; |
| } |
| config = configs_get_node_config(node, sub_node_name); |
| if (!config) { |
| /*release node refs. */ |
| err = -EPERM; |
| } else |
| *hold_node = node; |
| out: |
| if (config == NULL) { |
| if (node) { |
| configs_put_node(node, 1, root_node); |
| pr_err("can't find node %s's config:%s\n", |
| node->name, sub_node_name); |
| } else |
| pr_err("can't find node %s from %s\n", |
| sub_node_name, root_node->name); |
| } else { |
| *config_ret = config; |
| err = 0; |
| } |
| return err; |
| } |
| |
| static int configs_setget_config_value(struct mconfig *config, |
| const void *val_set, void *val_get, int size, int set) |
| { |
| int s; |
| int ret; |
| void *dst; |
| const void *src; |
| |
| if (set) { |
| dst = config->buf_ptr; |
| src = val_set; |
| } else { |
| dst = val_get; |
| src = config->buf_ptr; |
| } |
| if (!dst || !src) |
| return -1; |
| switch (config->type) { |
| case CONFIG_TYPE_PU32: |
| case CONFIG_TYPE_PI32: |
| if (size < sizeof(u32)) |
| return -2; |
| ((u32 *) dst)[0] = ((const u32 *)src)[0]; |
| ret = sizeof(u32); |
| break; |
| case CONFIG_TYPE_PU64: |
| if (size < sizeof(u64)) |
| return -2; |
| ((u64 *) dst)[0] = ((const u64 *)src)[0]; |
| ret = sizeof(u64); |
| break; |
| case CONFIG_TYPE_PCSTR: |
| if (set) { |
| ret = -3; |
| break; |
| } |
| /*rd same as CONFIG_TYPE_PSTR */ |
| case CONFIG_TYPE_PSTR: |
| s = min_t(int, size - 1, config->size - 1); |
| if (s > 0) |
| strncpy(dst, src, s); |
| else { |
| ret = -3; |
| break; |
| } |
| ret = s; |
| break; |
| case CONFIG_TYPE_U32: |
| case CONFIG_TYPE_I32: |
| if (size < sizeof(u32)) |
| return -2; |
| if (set) |
| config->u32val = ((const u32 *)src)[0]; |
| else |
| ((u32 *) dst)[0] = config->u32val; |
| ret = sizeof(u32); |
| break; |
| case CONFIG_TYPE_U64: |
| if (size < sizeof(u64)) |
| return -2; |
| if (set) |
| config->u64val = ((const u64 *)src)[0]; |
| else |
| ((u64 *) dst)[0] = config->u64val; |
| ret = sizeof(u64); |
| break; |
| default: |
| ret = -4; |
| } |
| return ret; |
| } |
| |
| static int configs_setget_node_path_value( |
| struct mconfig_node *topnode, |
| const char *path, |
| const void *val_set, void *val_get, |
| int size, int set) |
| { |
| struct mconfig_node *node; |
| struct mconfig *config; |
| int ret; |
| |
| ret = configs_get_node_path_config(topnode, &config, path, &node, set); |
| if (ret != 0) |
| return ret; |
| if (set) |
| pr_err("start set config val config=%s, val=%x\n", |
| config->item_name, *(int *)val_set); |
| ret = configs_setget_config_value(config, val_set, |
| val_get, size, set); |
| config_debug("setget config val config=%s, ret=%x end\n", |
| config->item_name, ret); |
| configs_put_node(node, 0, topnode); |
| configs_put_node(node, 1, topnode); |
| return ret; |
| } |
| |
| int configs_set_node_path_str(struct mconfig_node *topnode, |
| const char *path, const char *val) |
| { |
| struct mconfig_node *node; |
| struct mconfig *config; |
| int ret = -1; |
| |
| if (!val || !path || strlen(path) <= 0 || strlen(val) <= 0) |
| return -1; |
| ret = configs_get_node_path_config(topnode, &config, path, &node, 1); |
| if (ret != 0) |
| return ret; |
| config_debug("start set config val config=%s, %s\n", |
| config->item_name, val); |
| mutex_lock(&node->lock); |
| ret = configs_str2config(config, val); |
| mutex_unlock(&node->lock); |
| configs_put_node(node, 0, topnode); |
| configs_put_node(node, 1, topnode); |
| if (ret > 0) |
| ret = 0;/*set ok*/ |
| else if (ret == 0) |
| ret = -1;/*val not changed.*/ |
| return ret; |
| } |
| int configs_set_node_nodepath_str(struct mconfig_node *topnode, |
| const char *path, const char *val) |
| { |
| if (topnode == NULL) { |
| return configs_set_node_path_str(topnode, |
| path, val); |
| } |
| if (strncmp(topnode->name, path, strlen(topnode->name))) { |
| pr_err("nodepath(%s) must start from node=%s\n", |
| path, topnode->name); |
| return -1; |
| } |
| return configs_set_node_path_str(topnode, |
| path + strlen(topnode->name) + 1, val); |
| } |
| |
| int configs_set_prefix_path_str(const char *prefix, |
| const char *path, const char *str) |
| { |
| struct mconfig_node *topnode = NULL; |
| int ret; |
| |
| if (prefix && strlen(prefix) > 0) { |
| topnode = configs_get_node_path_end_node(NULL, prefix); |
| if (!topnode) { |
| pr_err("[0]can't get node from %s\n", prefix); |
| return -1; |
| } |
| } |
| ret = configs_set_node_path_str(topnode, path, str); |
| configs_put_node(topnode, 1, NULL); |
| return ret; |
| } |
| |
| int configs_set_node_path_valonpath( |
| struct mconfig_node *topnode, |
| const char *path) |
| { |
| return configs_set_node_path_str(topnode, path, |
| configs_parser_str_valstr(path)); |
| } |
| |
| int configs_set_prefix_path_valonpath(const char *prefix, |
| const char *path) |
| { |
| struct mconfig_node *topnode = NULL; |
| int ret; |
| |
| if (prefix && strlen(prefix) > 0) { |
| topnode = configs_get_node_path_end_node(NULL, prefix); |
| if (!topnode) { |
| pr_err("can't get node from %s\n", prefix); |
| return -1; |
| } |
| } |
| ret = configs_set_node_path_str(topnode, path, |
| configs_parser_str_valstr(path)); |
| configs_put_node(topnode, 1, NULL); |
| return ret; |
| } |
| |
| int configs_get_node_path_str(struct mconfig_node *topnode, |
| const char *path, char *buf, int size) |
| { |
| struct mconfig_node *node; |
| struct mconfig *config; |
| int ret = -1; |
| |
| if (!path || strlen(path) <= 0) |
| return -1; |
| ret = configs_get_node_path_config(topnode, |
| &config, path, &node, 0); |
| if (ret != 0) |
| return ret; |
| config_debug("startget config val config=%s\n", |
| config->item_name); |
| ret = configs_config2str(config, buf, size); |
| configs_put_node(node, 0, topnode); |
| configs_put_node(node, 1, topnode); |
| return ret; |
| } |
| int configs_get_node_nodepath_str(struct mconfig_node *topnode, |
| const char *path, char *buf, int size) |
| { |
| if (topnode == NULL) |
| return configs_get_node_path_str(topnode, path, buf, size); |
| if (strncmp(topnode->name, path, strlen(topnode->name))) { |
| pr_err("nodepath(%s) must start from node=%s\n", |
| path, topnode->name); |
| return -1; |
| } |
| return configs_get_node_path_str(topnode, |
| path + strlen(topnode->name) + 1, |
| buf, size); |
| |
| } |
| |
| static inline int configs_set_node_path_value( |
| struct mconfig_node *topnode, |
| const char *path, const void *val, int size) |
| { |
| return configs_setget_node_path_value(topnode, |
| path, val, NULL, size, 1); |
| } |
| |
| static int configs_get_node_path_value(struct mconfig_node *topnode, |
| const char *path, void *val, int size) |
| { |
| return configs_setget_node_path_value(topnode, |
| path, NULL, val, size, 0); |
| } |
| |
| int configs_get_node_path_u32(struct mconfig_node *topnode, |
| const char *path, u32 *val) |
| { |
| return configs_get_node_path_value(topnode, path, val, sizeof(u32)); |
| } |
| |
| int configs_get_node_path_u64(struct mconfig_node *topnode, |
| const char *path, u64 *val) |
| { |
| return configs_get_node_path_value(topnode, path, val, sizeof(u64)); |
| } |
| |
| int configs_set_node_path_u32(struct mconfig_node *topnode, |
| const char *path, u32 val) |
| { |
| return configs_set_node_path_value(topnode, path, &val, sizeof(u32)); |
| } |
| |
| int configs_set_node_path_u64(struct mconfig_node *topnode, |
| const char *path, u64 val) |
| { |
| return configs_set_node_path_value(topnode, path, &val, |
| sizeof(u64)); |
| } |
| |
| int configs_config_system_init(void) |
| { |
| configs_init_new_node(&root_node, "root", CONFIG_FOR_RW); |
| configs_register_node(NULL, &root_node); |
| return 0; |
| } |