478 lines
10 KiB
C
Executable File
478 lines
10 KiB
C
Executable File
|
|
#include "include/sec_battery_vote.h"
|
|
#include <linux/slab.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/debugfs.h>
|
|
|
|
static struct dentry *debug_root;
|
|
static struct dentry *status_all;
|
|
static LIST_HEAD(vote_list);
|
|
static struct mutex vote_lock;
|
|
struct sec_voter {
|
|
int enable;
|
|
int value;
|
|
};
|
|
struct sec_vote {
|
|
const char * name;
|
|
int type;
|
|
int num;
|
|
struct sec_voter *voter;
|
|
const char ** voter_name;
|
|
int id;
|
|
int res;
|
|
int init_val;
|
|
struct mutex lock;
|
|
void * data;
|
|
int(*cb)(void *data, int value);
|
|
struct list_head list;
|
|
|
|
struct dentry * root;
|
|
struct dentry * status_ent;
|
|
int force_set;
|
|
int force_val;
|
|
struct dentry * force_set_ent;
|
|
struct dentry * force_val_ent;
|
|
};
|
|
|
|
const char * none_str = "None";
|
|
const char * force_str = "Force";
|
|
|
|
static int select_min(struct sec_voter * voter, int max, int * id, int * res)
|
|
{
|
|
int i;
|
|
*res = INT_MAX;
|
|
*id = -EINVAL;
|
|
for (i = 0; i < max; i++) {
|
|
if (voter[i].enable && *res > voter[i].value) {
|
|
*res = voter[i].value;
|
|
*id = i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static int select_max(struct sec_voter * voter, int max, int * id, int * res)
|
|
{
|
|
int i;
|
|
*res = INT_MIN;
|
|
*id = -EINVAL;
|
|
for (i = 0; i < max; i++) {
|
|
if (voter[i].enable && *res < voter[i].value) {
|
|
*res = voter[i].value;
|
|
*id = i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
static int select_enable(struct sec_voter * voter, int max, int * id, int * res)
|
|
{
|
|
int i;
|
|
*res = 0;
|
|
*id = -EINVAL;
|
|
for (i = 0; i < max; i++) {
|
|
if (voter[i].enable) {
|
|
*res = voter[i].enable;
|
|
*id = i;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
int get_sec_vote(struct sec_vote * vote, const char ** name, int * value)
|
|
{
|
|
mutex_lock(&vote->lock);
|
|
if (vote->id >= 0) {
|
|
*name = vote->voter_name[vote->id];
|
|
}
|
|
else {
|
|
*name = none_str;
|
|
}
|
|
*value = vote->res;
|
|
mutex_unlock(&vote->lock);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(get_sec_vote);
|
|
int get_sec_vote_result(struct sec_vote *vote)
|
|
{
|
|
int v;
|
|
mutex_lock(&vote->lock);
|
|
if (vote->force_set)
|
|
v = vote->force_val;
|
|
else
|
|
v = vote->res;
|
|
mutex_unlock(&vote->lock);
|
|
return v;
|
|
}
|
|
EXPORT_SYMBOL(get_sec_vote_result);
|
|
|
|
const char* get_sec_keyvoter_name(struct sec_vote *vote)
|
|
{
|
|
const char * str;
|
|
mutex_lock(&vote->lock);
|
|
if (vote->force_set)
|
|
str = force_str;
|
|
else
|
|
str = (vote->id >= 0)?vote->voter_name[vote->id]: none_str;
|
|
mutex_unlock(&vote->lock);
|
|
return str;
|
|
}
|
|
EXPORT_SYMBOL(get_sec_keyvoter_name);
|
|
|
|
int get_sec_voter_status(struct sec_vote *vote, int id, int * v)
|
|
{
|
|
if (id >= vote->num || id < 0)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&vote->lock);
|
|
if (vote->type == SEC_VOTE_EN)
|
|
*v = vote->voter[id].enable;
|
|
else if (vote->voter[id].enable)
|
|
*v = vote->voter[id].value;
|
|
else
|
|
*v = INT_MIN;
|
|
mutex_unlock(&vote->lock);
|
|
return (*v == INT_MIN) ? -EINVAL : 0;
|
|
}
|
|
EXPORT_SYMBOL(get_sec_voter_status);
|
|
|
|
static int show_vote_clients(struct seq_file *m, void *data)
|
|
{
|
|
struct sec_vote *vote = m->private;
|
|
int i;
|
|
char *type_str = "Unkonwn";
|
|
|
|
mutex_lock(&vote->lock);
|
|
for (i = 0; i < vote->num; i++) {
|
|
if (vote->voter[i].enable) {
|
|
seq_printf(m, "%s: %s:\t\t\ten=%d v=%d\n",
|
|
vote->name,
|
|
vote->voter_name[i],
|
|
vote->voter[i].enable,
|
|
vote->voter[i].value);
|
|
}
|
|
}
|
|
switch (vote->type) {
|
|
case SEC_VOTE_MIN:
|
|
type_str = "Min";
|
|
break;
|
|
case SEC_VOTE_MAX:
|
|
type_str = "Max";
|
|
break;
|
|
case SEC_VOTE_EN:
|
|
type_str = "Set_any";
|
|
break;
|
|
default:
|
|
type_str = "Invalid";
|
|
}
|
|
seq_printf(m, "%s: INIT: v=%d\n",
|
|
vote->name, vote->init_val);
|
|
if (vote->force_set) {
|
|
seq_printf(m, "%s: voter=%s type=%s v=%d\n",
|
|
vote->name, force_str, type_str, vote->force_val);
|
|
} else {
|
|
seq_printf(m, "%s: voter=%s type=%s v=%d\n",
|
|
vote->name,
|
|
(vote->id >= 0)?vote->voter_name[vote->id]: none_str,
|
|
type_str, vote->res);
|
|
}
|
|
mutex_unlock(&vote->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vote_status_open(struct inode *inode, struct file *file)
|
|
{
|
|
struct sec_vote *vote = inode->i_private;
|
|
|
|
return single_open(file, show_vote_clients, vote);
|
|
}
|
|
|
|
static const struct file_operations vote_status_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = vote_status_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int show_all_clients(struct seq_file *m, void *data)
|
|
{
|
|
struct sec_vote *vote;
|
|
int i;
|
|
char *type_str = "Unkonwn";
|
|
|
|
if (list_empty(&vote_list)) {
|
|
seq_printf(m, "No vote\n");
|
|
return 0;
|
|
}
|
|
|
|
mutex_lock(&vote_lock);
|
|
list_for_each_entry(vote, &vote_list, list) {
|
|
mutex_lock(&vote->lock);
|
|
for (i = 0; i < vote->num; i++) {
|
|
if (vote->voter[i].enable) {
|
|
seq_printf(m, "%s: %s:\t\t\ten=%d v=%d\n",
|
|
vote->name,
|
|
vote->voter_name[i],
|
|
vote->voter[i].enable,
|
|
vote->voter[i].value);
|
|
}
|
|
}
|
|
switch (vote->type) {
|
|
case SEC_VOTE_MIN:
|
|
type_str = "Min";
|
|
break;
|
|
case SEC_VOTE_MAX:
|
|
type_str = "Max";
|
|
break;
|
|
case SEC_VOTE_EN:
|
|
type_str = "Set_any";
|
|
break;
|
|
default:
|
|
type_str = "Invalid";
|
|
}
|
|
seq_printf(m, "%s: INIT: v=%d\n",
|
|
vote->name, vote->init_val);
|
|
if (vote->force_set) {
|
|
seq_printf(m, "%s: voter=%s type=%s v=%d\n",
|
|
vote->name, force_str, type_str, vote->force_val);
|
|
} else {
|
|
seq_printf(m, "%s: voter=%s type=%s v=%d\n",
|
|
vote->name,
|
|
(vote->id >= 0)?vote->voter_name[vote->id]: none_str,
|
|
type_str, vote->res);
|
|
}
|
|
mutex_unlock(&vote->lock);
|
|
}
|
|
mutex_unlock(&vote_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vote_status_all_open(struct inode *inode, struct file *file)
|
|
{
|
|
|
|
return single_open(file, show_all_clients, NULL);
|
|
}
|
|
|
|
static const struct file_operations vote_status_all_ops = {
|
|
.owner = THIS_MODULE,
|
|
.open = vote_status_all_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static int force_get(void *data, u64 *val)
|
|
{
|
|
struct sec_vote *vote = data;
|
|
|
|
*val = vote->force_set;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int force_set(void *data, u64 val)
|
|
{
|
|
struct sec_vote *vote = data;
|
|
|
|
mutex_lock(&vote->lock);
|
|
vote->force_set = val;
|
|
|
|
if (!vote->cb)
|
|
goto out;
|
|
|
|
if (vote->force_set) {
|
|
vote->cb(vote->data, vote->force_val);
|
|
} else {
|
|
vote->cb(vote->data, vote->res);
|
|
}
|
|
out:
|
|
mutex_unlock(&vote->lock);
|
|
return 0;
|
|
}
|
|
DEFINE_SIMPLE_ATTRIBUTE(vote_force_ops, force_get, force_set, "%lld\n");
|
|
struct sec_vote *find_vote(const char *name)
|
|
{
|
|
struct sec_vote *vote;
|
|
|
|
list_for_each_entry(vote, &vote_list, list) {
|
|
if (strcmp(vote->name, name) == 0) {
|
|
return vote;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
struct sec_vote * sec_vote_init(const char * name, int type, int num, int init_val,
|
|
const char ** voter_name, int(*cb)(void *data, int value), void * data)
|
|
{
|
|
static int init = false;
|
|
struct sec_vote * vote = NULL;
|
|
struct sec_voter * voter = NULL;
|
|
|
|
if (!init) {
|
|
pr_info("%s: Init \n", __func__);
|
|
init = true;
|
|
mutex_init(&vote_lock);
|
|
}
|
|
mutex_lock(&vote_lock);
|
|
vote = find_vote(name);
|
|
if (vote) {
|
|
pr_info("%s: %s exist \n", __func__, name);
|
|
goto err;
|
|
}
|
|
if (voter_name == NULL) {
|
|
pr_info("%s: Please add voter name list \n", __func__);
|
|
goto err;
|
|
}
|
|
|
|
vote = kzalloc(sizeof(struct sec_vote), GFP_KERNEL);
|
|
if (!vote) {
|
|
pr_info("%s: mem aloocate fail \n", __func__);
|
|
goto err;
|
|
}
|
|
vote->name = name;
|
|
vote->type = type;
|
|
voter = kzalloc(sizeof(struct sec_voter) * num, GFP_KERNEL);
|
|
if (!voter) {
|
|
pr_info("%s: mem aloocate fail \n", __func__);
|
|
kfree(vote);
|
|
goto err;
|
|
}
|
|
vote->voter = voter;
|
|
vote->num = num;
|
|
vote->voter_name = voter_name;
|
|
vote->init_val = init_val;
|
|
vote->cb = cb;
|
|
vote->id = -EINVAL;
|
|
vote->res = -EINVAL;
|
|
vote->data = data;
|
|
mutex_init(&vote->lock);
|
|
|
|
if (debug_root == NULL) {
|
|
debug_root = debugfs_create_dir("sec-vote", NULL);
|
|
if (!debug_root) {
|
|
pr_err("Couldn't create debug dir\n");
|
|
} else {
|
|
status_all = debugfs_create_file("status_all",
|
|
S_IFREG | 0444,
|
|
debug_root, NULL,
|
|
&vote_status_all_ops);
|
|
if (!status_all) {
|
|
pr_err("Couldn't create status_all dbg file \n");
|
|
}
|
|
}
|
|
}
|
|
if (debug_root)
|
|
vote->root = debugfs_create_dir(name, debug_root);
|
|
if (!vote->root) {
|
|
pr_err("Couldn't create debug dir %s\n", name);
|
|
} else {
|
|
vote->status_ent = debugfs_create_file("status", S_IFREG | 0444,
|
|
vote->root, vote,
|
|
&vote_status_ops);
|
|
if (!vote->status_ent) {
|
|
pr_err("Couldn't create status dbg file for %s\n", name);
|
|
}
|
|
|
|
vote->force_val_ent = debugfs_create_u32("force_val",
|
|
S_IFREG | 0644,
|
|
vote->root, &(vote->force_val));
|
|
if (!vote->force_val_ent) {
|
|
pr_err("Couldn't create force_val dbg file for %s\n", name);
|
|
}
|
|
|
|
vote->force_set_ent = debugfs_create_file("force_set",
|
|
S_IFREG | 0444,
|
|
vote->root, vote,
|
|
&vote_force_ops);
|
|
if (!vote->force_set_ent) {
|
|
pr_err("Couldn't create force_set dbg file for %s\n", name);
|
|
}
|
|
}
|
|
pr_info("%s: %s \n", __func__, name);
|
|
list_add(&vote->list, &vote_list);
|
|
mutex_unlock(&vote_lock);
|
|
return vote;
|
|
err:
|
|
mutex_unlock(&vote_lock);
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(sec_vote_init);
|
|
|
|
void sec_vote_destroy(struct sec_vote * vote)
|
|
{
|
|
pr_info("%s: %s\n", __func__, vote->name);
|
|
list_del(&vote->list);
|
|
kfree(vote->voter);
|
|
debugfs_remove_recursive(vote->root);
|
|
mutex_destroy(&vote->lock);
|
|
mutex_destroy(&vote_lock);
|
|
kfree(vote);
|
|
}
|
|
EXPORT_SYMBOL(sec_vote_destroy);
|
|
|
|
void sec_vote(struct sec_vote * vote, int event, int en, int value)
|
|
{
|
|
int id, res;
|
|
|
|
if (event >= vote->num) {
|
|
pr_info("%s id Error(%d)\n", __func__, event);
|
|
return;
|
|
}
|
|
mutex_lock(&vote->lock);
|
|
pr_debug("%s, %s en: %d->%d, v: %d->%d\n", vote->name,vote->voter_name[event],
|
|
vote->voter[event].enable, en, vote->voter[event].value, value);
|
|
vote->voter[event].enable = en;
|
|
vote->voter[event].value = value;
|
|
switch (vote->type) {
|
|
case SEC_VOTE_MIN:
|
|
select_min(vote->voter, vote->num, &id, &res);
|
|
if (res == INT_MAX)
|
|
res = vote->init_val;
|
|
break;
|
|
case SEC_VOTE_MAX:
|
|
select_max(vote->voter, vote->num, &id, &res);
|
|
if (res == INT_MIN)
|
|
res = vote->init_val;
|
|
break;
|
|
case SEC_VOTE_EN:
|
|
select_enable(vote->voter, vote->num, &id, &res);
|
|
break;
|
|
default:
|
|
pr_err("%s type invalid\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
if (res != vote->res) {
|
|
pr_info("%s: %s (%s, %d) -> (%s, %d)\n", __func__, vote->name,
|
|
(vote->id >= 0)?vote->voter_name[vote->id]: none_str, vote->res,
|
|
(id>=0)? vote->voter_name[id] : none_str, res);
|
|
vote->id = id;
|
|
vote->res = res;
|
|
if (vote->force_set)
|
|
pr_err("%s skip by force_set\n", __func__);
|
|
else
|
|
vote->cb(vote->data, res);
|
|
}
|
|
out:
|
|
mutex_unlock(&vote->lock);
|
|
}
|
|
EXPORT_SYMBOL(sec_vote);
|
|
|
|
void sec_vote_refresh(struct sec_vote * vote)
|
|
{
|
|
mutex_lock(&vote->lock);
|
|
pr_info("%s refresh (%s, %d)\n", vote->name,
|
|
(vote->id >= 0)?vote->voter_name[vote->id]: none_str, vote->res);
|
|
vote->cb(vote->data, vote->res);
|
|
mutex_unlock(&vote->lock);
|
|
}
|
|
EXPORT_SYMBOL(sec_vote_refresh);
|
|
|
|
const char * get_sec_vote_name(struct sec_vote * vote)
|
|
{
|
|
return vote->name;
|
|
}
|
|
EXPORT_SYMBOL(get_sec_vote_name);
|
|
|