Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7ea2937 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "pmic-voter: bump up from 12 clients to 16"

parents 617deda0 e7ed882a
Loading
Loading
Loading
Loading
+538 −110
Original line number Diff line number Diff line
/* Copyright (c) 2015 The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017 The Linux Foundation. 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 version 2 and
@@ -10,80 +10,171 @@
 * GNU General Public License for more details.
 */

#include <linux/debugfs.h>
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/bitops.h>
#include <linux/printk.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/string.h>

#include "pmic-voter.h"
#include <linux/pmic-voter.h>

#define NUM_MAX_CLIENTS	12
#define NUM_MAX_CLIENTS		16
#define DEBUG_FORCE_CLIENT	"DEBUG_FORCE_CLIENT"

static DEFINE_SPINLOCK(votable_list_slock);
static LIST_HEAD(votable_list);

static struct dentry *debug_root;

struct client_vote {
	int	state;
	bool	enabled;
	int	value;
};

struct votable {
	struct client_vote	votes[NUM_MAX_CLIENTS];
	struct device		*dev;
	const char		*name;
	struct list_head	list;
	struct client_vote	votes[NUM_MAX_CLIENTS];
	int			num_clients;
	int			type;
	int			effective_client_id;
	int			effective_result;
	int			default_result;
	struct mutex		vote_lock;
	int			(*callback)(struct device *dev,
	void			*data;
	int			(*callback)(struct votable *votable,
						void *data,
						int effective_result,
						int effective_client,
						int last_result,
						int last_client);
						const char *effective_client);
	char			*client_strs[NUM_MAX_CLIENTS];
	bool			voted_on;
	struct dentry		*root;
	struct dentry		*status_ent;
	u32			force_val;
	struct dentry		*force_val_ent;
	bool			force_active;
	struct dentry		*force_active_ent;
};

static int vote_set_any(struct votable *votable)
/**
 * vote_set_any()
 * @votable:	votable object
 * @client_id:	client number of the latest voter
 * @eff_res:	sets 0 or 1 based on the voting
 * @eff_id:	Always returns the client_id argument
 *
 * Note that for SET_ANY voter, the value is always same as enabled. There is
 * no idea of a voter abstaining from the election. Hence there is never a
 * situation when the effective_id will be invalid, during election.
 *
 * Context:
 *	Must be called with the votable->lock held
 */
static void vote_set_any(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int i;

	for (i = 0; i < votable->num_clients; i++)
		if (votable->votes[i].state == 1)
			return 1;
	return 0;
	*eff_res = 0;

	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++)
		*eff_res |= votable->votes[i].enabled;

	*eff_id = client_id;
}

static int vote_min(struct votable *votable)
/**
 * vote_min() -
 * @votable:	votable object
 * @client_id:	client number of the latest voter
 * @eff_res:	sets this to the min. of all the values amongst enabled voters.
 *		If there is no enabled client, this is set to INT_MAX
 * @eff_id:	sets this to the client id that has the min value amongst all
 *		the enabled clients. If there is no enabled client, sets this
 *		to -EINVAL
 *
 * Context:
 *	Must be called with the votable->lock held
 */
static void vote_min(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int min_vote = INT_MAX;
	int client_index = -EINVAL;
	int i;

	for (i = 0; i < votable->num_clients; i++) {
		if (votable->votes[i].state == 1 &&
				min_vote > votable->votes[i].value) {
			min_vote = votable->votes[i].value;
			client_index = i;
	*eff_res = INT_MAX;
	*eff_id = -EINVAL;
	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) {
		if (votable->votes[i].enabled
			&& *eff_res > votable->votes[i].value) {
			*eff_res = votable->votes[i].value;
			*eff_id = i;
		}
	}
	if (*eff_id == -EINVAL)
		*eff_res = -EINVAL;
}

	return client_index;
/**
 * vote_max() -
 * @votable:	votable object
 * @client_id:	client number of the latest voter
 * @eff_res:	sets this to the max. of all the values amongst enabled voters.
 *		If there is no enabled client, this is set to -EINVAL
 * @eff_id:	sets this to the client id that has the max value amongst all
 *		the enabled clients. If there is no enabled client, sets this to
 *		-EINVAL
 *
 * Context:
 *	Must be called with the votable->lock held
 */
static void vote_max(struct votable *votable, int client_id,
				int *eff_res, int *eff_id)
{
	int i;

	*eff_res = INT_MIN;
	*eff_id = -EINVAL;
	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) {
		if (votable->votes[i].enabled &&
				*eff_res < votable->votes[i].value) {
			*eff_res = votable->votes[i].value;
			*eff_id = i;
		}
	}
	if (*eff_id == -EINVAL)
		*eff_res = -EINVAL;
}

static int vote_max(struct votable *votable)
static int get_client_id(struct votable *votable, const char *client_str)
{
	int max_vote = INT_MIN;
	int client_index = -EINVAL;
	int i;

	for (i = 0; i < votable->num_clients; i++) {
		if (votable->votes[i].state == 1 &&
				max_vote < votable->votes[i].value) {
			max_vote = votable->votes[i].value;
			client_index = i;
		if (votable->client_strs[i]
		 && (strcmp(votable->client_strs[i], client_str) == 0))
			return i;
	}

	/* new client */
	for (i = 0; i < votable->num_clients; i++) {
		if (!votable->client_strs[i]) {
			votable->client_strs[i]
				= kstrdup(client_str, GFP_KERNEL);
			if (!votable->client_strs[i])
				return -ENOMEM;
			return i;
		}
	}
	return -EINVAL;
}

	return client_index;
static char *get_client_str(struct votable *votable, int client_id)
{
	if (client_id == -EINVAL)
		return NULL;

	 return votable->client_strs[client_id];
}

void lock_votable(struct votable *votable)
@@ -96,160 +187,429 @@ void unlock_votable(struct votable *votable)
	mutex_unlock(&votable->vote_lock);
}

int get_client_vote(struct votable *votable, int client_id)
/**
 * is_client_vote_enabled() -
 * is_client_vote_enabled_locked() -
 *		The unlocked and locked variants of getting whether a client's
		vote is enabled.
 * @votable:	the votable object
 * @client_str: client of interest
 *
 * Returns:
 *	True if the client's vote is enabled; false otherwise.
 */
bool is_client_vote_enabled_locked(struct votable *votable,
							const char *client_str)
{
	int value;
	int client_id = get_client_id(votable, client_str);

	if (client_id < 0)
		return false;

	return votable->votes[client_id].enabled;
}

bool is_client_vote_enabled(struct votable *votable, const char *client_str)
{
	bool enabled;

	lock_votable(votable);
	value = get_client_vote_locked(votable, client_id);
	enabled = is_client_vote_enabled_locked(votable, client_str);
	unlock_votable(votable);
	return value;
	return enabled;
}

int get_client_vote_locked(struct votable *votable, int client_id)
/**
 * get_client_vote() -
 * get_client_vote_locked() -
 *		The unlocked and locked variants of getting a client's voted
 *		value.
 * @votable:	the votable object
 * @client_str: client of interest
 *
 * Returns:
 *	The value the client voted for. -EINVAL is returned if the client
 *	is not enabled or the client is not found.
 */
int get_client_vote_locked(struct votable *votable, const char *client_str)
{
	if (votable->votes[client_id].state < 0)
		return votable->default_result;
	int client_id = get_client_id(votable, client_str);

	if (client_id < 0)
		return -EINVAL;

	if ((votable->type != VOTE_SET_ANY)
		&& !votable->votes[client_id].enabled)
		return -EINVAL;

	return votable->votes[client_id].value;
}

int get_effective_result(struct votable *votable)
int get_client_vote(struct votable *votable, const char *client_str)
{
	int value;

	lock_votable(votable);
	value = get_effective_result_locked(votable);
	value = get_client_vote_locked(votable, client_str);
	unlock_votable(votable);
	return value;
}

/**
 * get_effective_result() -
 * get_effective_result_locked() -
 *		The unlocked and locked variants of getting the effective value
 *		amongst all the enabled voters.
 *
 * @votable:	the votable object
 *
 * Returns:
 *	The effective result.
 *	For MIN and MAX votable, returns -EINVAL when the votable
 *	object has been created but no clients have casted their votes or
 *	the last enabled client disables its vote.
 *	For SET_ANY votable it returns 0 when no clients have casted their votes
 *	because for SET_ANY there is no concept of abstaining from election. The
 *	votes for all the clients of SET_ANY votable is defaulted to false.
 */
int get_effective_result_locked(struct votable *votable)
{
	if (votable->effective_result < 0)
		return votable->default_result;
	if (votable->force_active)
		return votable->force_val;

	return votable->effective_result;
}

int get_effective_client_id(struct votable *votable)
int get_effective_result(struct votable *votable)
{
	int id;
	int value;

	lock_votable(votable);
	id = get_effective_client_id_locked(votable);
	value = get_effective_result_locked(votable);
	unlock_votable(votable);
	return id;
	return value;
}

/**
 * get_effective_client() -
 * get_effective_client_locked() -
 *		The unlocked and locked variants of getting the effective client
 *		amongst all the enabled voters.
 *
 * @votable:	the votable object
 *
 * Returns:
 *	The effective client.
 *	For MIN and MAX votable, returns NULL when the votable
 *	object has been created but no clients have casted their votes or
 *	the last enabled client disables its vote.
 *	For SET_ANY votable it returns NULL too when no clients have casted
 *	their votes. But for SET_ANY since there is no concept of abstaining
 *	from election, the only client that casts a vote or the client that
 *	caused the result to change is returned.
 */
const char *get_effective_client_locked(struct votable *votable)
{
	if (votable->force_active)
		return DEBUG_FORCE_CLIENT;

	return get_client_str(votable, votable->effective_client_id);
}

int get_effective_client_id_locked(struct votable *votable)
const char *get_effective_client(struct votable *votable)
{
	return votable->effective_client_id;
	const char *client_str;

	lock_votable(votable);
	client_str = get_effective_client_locked(votable);
	unlock_votable(votable);
	return client_str;
}

int vote(struct votable *votable, int client_id, bool state, int val)
/**
 * vote() -
 *
 * @votable:	the votable object
 * @client_str: the voting client
 * @enabled:	This provides a means for the client to exclude himself from
 *		election. This clients val (the next argument) will be
 *		considered only when he has enabled his participation.
 *		Note that this takes a differnt meaning for SET_ANY type, as
 *		there is no concept of abstaining from participation.
 *		Enabled is treated as the boolean value the client is voting.
 * @val:	The vote value. This is ignored for SET_ANY votable types.
 *		For MIN, MAX votable types this value is used as the
 *		clients vote value when the enabled is true, this value is
 *		ignored if enabled is false.
 *
 * The callback is called only when there is a change in the election results or
 * if it is the first time someone is voting.
 *
 * Returns:
 *	The return from the callback when present and needs to be called
 *	or zero.
 */
int vote(struct votable *votable, const char *client_str, bool enabled, int val)
{
	int effective_id, effective_result;
	int effective_id = -EINVAL;
	int effective_result;
	int client_id;
	int rc = 0;
	bool similar_vote = false;

	lock_votable(votable);

	if (votable->votes[client_id].state == state &&
				votable->votes[client_id].value == val) {
		pr_debug("%s: votes unchanged; skipping\n", votable->name);
	client_id = get_client_id(votable, client_str);
	if (client_id < 0) {
		rc = client_id;
		goto out;
	}

	votable->votes[client_id].state = state;
	/*
	 * for SET_ANY the val is to be ignored, set it
	 * to enabled so that the election still works based on
	 * value regardless of the type
	 */
	if (votable->type == VOTE_SET_ANY)
		val = enabled;

	if ((votable->votes[client_id].enabled == enabled) &&
		(votable->votes[client_id].value == val)) {
		pr_debug("%s: %s,%d same vote %s of val=%d\n",
				votable->name,
				client_str, client_id,
				enabled ? "on" : "off",
				val);
		similar_vote = true;
	}

	votable->votes[client_id].enabled = enabled;
	votable->votes[client_id].value = val;

	pr_debug("%s: %d voting for %d - %s\n",
	if (similar_vote && votable->voted_on) {
		pr_debug("%s: %s,%d Ignoring similar vote %s of val=%d\n",
			votable->name,
			client_id, val, state ? "on" : "off");
			client_str, client_id, enabled ? "on" : "off", val);
		goto out;
	}

	pr_debug("%s: %s,%d voting %s of val=%d\n",
		votable->name,
		client_str, client_id, enabled ? "on" : "off", val);
	switch (votable->type) {
	case VOTE_MIN:
		effective_id = vote_min(votable);
		vote_min(votable, client_id, &effective_result, &effective_id);
		break;
	case VOTE_MAX:
		effective_id = vote_max(votable);
		vote_max(votable, client_id, &effective_result, &effective_id);
		break;
	case VOTE_SET_ANY:
		votable->votes[client_id].value = state;
		effective_result = vote_set_any(votable);
		if (effective_result != votable->effective_result) {
			votable->effective_client_id = client_id;
			votable->effective_result = effective_result;
			rc = votable->callback(votable->dev,
						effective_result, client_id,
						state, client_id);
		}
		goto out;
		vote_set_any(votable, client_id,
				&effective_result, &effective_id);
		break;
	default:
		return -EINVAL;
	}

	/*
	 * If the votable does not have any votes it will maintain the last
	 * known effective_result and effective_client_id
	 * Note that the callback is called with a NULL string and -EINVAL
	 * result when there are no enabled votes
	 */
	if (effective_id < 0) {
		pr_debug("%s: no votes; skipping callback\n", votable->name);
	if (!votable->voted_on
			|| (effective_result != votable->effective_result)) {
		votable->effective_client_id = effective_id;
		votable->effective_result = effective_result;
		pr_debug("%s: effective vote is now %d voted by %s,%d\n",
			votable->name, effective_result,
			get_client_str(votable, effective_id),
			effective_id);
		if (votable->callback && !votable->force_active)
			rc = votable->callback(votable, votable->data,
					effective_result,
					get_client_str(votable, effective_id));
	}

	votable->voted_on = true;
out:
	unlock_votable(votable);
	return rc;
}

int rerun_election(struct votable *votable)
{
	int rc = 0;

	lock_votable(votable);
	if (votable->callback)
		rc = votable->callback(votable,
				votable->data,
			votable->effective_result,
			get_client_str(votable, votable->effective_client_id));
	unlock_votable(votable);
	return rc;
}

struct votable *find_votable(const char *name)
{
	unsigned long flags;
	struct votable *v;
	bool found = false;

	spin_lock_irqsave(&votable_list_slock, flags);
	if (list_empty(&votable_list))
		goto out;

	list_for_each_entry(v, &votable_list, list) {
		if (strcmp(v->name, name) == 0) {
			found = true;
			break;
		}
	}
out:
	spin_unlock_irqrestore(&votable_list_slock, flags);

	effective_result = votable->votes[effective_id].value;
	if (found)
		return v;
	else
		return NULL;
}

	if (effective_result != votable->effective_result) {
		votable->effective_client_id = effective_id;
		votable->effective_result = effective_result;
		pr_debug("%s: effective vote is now %d voted by %d\n",
				votable->name, effective_result, effective_id);
		rc = votable->callback(votable->dev, effective_result,
					effective_id, val, client_id);
static int force_active_get(void *data, u64 *val)
{
	struct votable *votable = data;

	*val = votable->force_active;

	return 0;
}

static int force_active_set(void *data, u64 val)
{
	struct votable *votable = data;
	int rc = 0;

	lock_votable(votable);
	votable->force_active = !!val;

	if (!votable->callback)
		goto out;

	if (votable->force_active) {
		rc = votable->callback(votable, votable->data,
			votable->force_val,
			DEBUG_FORCE_CLIENT);
	} else {
		rc = votable->callback(votable, votable->data,
			votable->effective_result,
			get_client_str(votable, votable->effective_client_id));
	}
out:
	unlock_votable(votable);
	return rc;
}
DEFINE_SIMPLE_ATTRIBUTE(votable_force_ops, force_active_get, force_active_set,
		"%lld\n");

static int show_votable_clients(struct seq_file *m, void *data)
{
	struct votable *votable = m->private;
	int i;
	char *type_str = "Unkonwn";
	const char *effective_client_str;

	lock_votable(votable);

	seq_printf(m, "Votable %s:\n", votable->name);
	seq_puts(m, "clients:\n");
	for (i = 0; i < votable->num_clients; i++) {
		if (votable->client_strs[i]) {
			seq_printf(m, "%-15s:\t\ten=%d\t\tv=%d\n",
					votable->client_strs[i],
					votable->votes[i].enabled,
					votable->votes[i].value);
		}
	}

struct votable *create_votable(struct device *dev, const char *name,
	switch (votable->type) {
	case VOTE_MIN:
		type_str = "Min";
		break;
	case VOTE_MAX:
		type_str = "Max";
		break;
	case VOTE_SET_ANY:
		type_str = "Set_any";
		break;
	}

	seq_printf(m, "type: %s\n", type_str);
	seq_puts(m, "Effective:\n");
	effective_client_str = get_effective_client_locked(votable);
	seq_printf(m, "%-15s:\t\tv=%d\n",
			effective_client_str ? effective_client_str : "none",
			get_effective_result_locked(votable));
	unlock_votable(votable);

	return 0;
}

static int votable_status_open(struct inode *inode, struct file *file)
{
	struct votable *votable = inode->i_private;

	return single_open(file, show_votable_clients, votable);
}

static const struct file_operations votable_status_ops = {
	.owner		= THIS_MODULE,
	.open		= votable_status_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

struct votable *create_votable(const char *name,
				int votable_type,
					int num_clients,
					int default_result,
					int (*callback)(struct device *dev,
				int (*callback)(struct votable *votable,
					void *data,
					int effective_result,
							int effective_client,
							int last_result,
							int last_client)
					)
					const char *effective_client),
				void *data)
{
	int i;
	struct votable *votable;
	unsigned long flags;

	if (!callback) {
		dev_err(dev, "Invalid callback specified for voter\n");
		return ERR_PTR(-EINVAL);
	}
	votable = find_votable(name);
	if (votable)
		return ERR_PTR(-EEXIST);

	if (votable_type >= NUM_VOTABLE_TYPES) {
		dev_err(dev, "Invalid votable_type specified for voter\n");
		return ERR_PTR(-EINVAL);
	if (debug_root == NULL) {
		debug_root = debugfs_create_dir("pmic-votable", NULL);
		if (!debug_root) {
			pr_err("Couldn't create debug dir\n");
			return ERR_PTR(-ENOMEM);
		}
	}

	if (num_clients > NUM_MAX_CLIENTS) {
		dev_err(dev, "Invalid num_clients specified for voter\n");
	if (votable_type >= NUM_VOTABLE_TYPES) {
		pr_err("Invalid votable_type specified for voter\n");
		return ERR_PTR(-EINVAL);
	}

	votable = devm_kzalloc(dev, sizeof(struct votable), GFP_KERNEL);
	votable = kzalloc(sizeof(struct votable), GFP_KERNEL);
	if (!votable)
		return ERR_PTR(-ENOMEM);

	votable->dev = dev;
	votable->name = name;
	votable->num_clients = num_clients;
	votable->name = kstrdup(name, GFP_KERNEL);
	if (!votable->name) {
		kfree(votable);
		return ERR_PTR(-ENOMEM);
	}

	votable->num_clients = NUM_MAX_CLIENTS;
	votable->callback = callback;
	votable->type = votable_type;
	votable->default_result = default_result;
	votable->data = data;
	mutex_init(&votable->vote_lock);

	/*
@@ -257,10 +617,78 @@ struct votable *create_votable(struct device *dev, const char *name,
	 * before the first vote, initialize them to -EINVAL
	 */
	votable->effective_result = -EINVAL;
	if (votable->type == VOTE_SET_ANY)
		votable->effective_result = 0;
	votable->effective_client_id = -EINVAL;

	for (i = 0; i < votable->num_clients; i++)
		votable->votes[i].state = -EINVAL;
	spin_lock_irqsave(&votable_list_slock, flags);
	list_add(&votable->list, &votable_list);
	spin_unlock_irqrestore(&votable_list_slock, flags);

	votable->root = debugfs_create_dir(name, debug_root);
	if (!votable->root) {
		pr_err("Couldn't create debug dir %s\n", name);
		kfree(votable->name);
		kfree(votable);
		return ERR_PTR(-ENOMEM);
	}

	votable->status_ent = debugfs_create_file("status", S_IFREG | S_IRUGO,
				  votable->root, votable,
				  &votable_status_ops);
	if (!votable->status_ent) {
		pr_err("Couldn't create status dbg file for %s\n", name);
		debugfs_remove_recursive(votable->root);
		kfree(votable->name);
		kfree(votable);
		return ERR_PTR(-EEXIST);
	}

	votable->force_val_ent = debugfs_create_u32("force_val",
					S_IFREG | S_IWUSR | S_IRUGO,
					votable->root,
					&(votable->force_val));

	if (!votable->force_val_ent) {
		pr_err("Couldn't create force_val dbg file for %s\n", name);
		debugfs_remove_recursive(votable->root);
		kfree(votable->name);
		kfree(votable);
		return ERR_PTR(-EEXIST);
	}

	votable->force_active_ent = debugfs_create_file("force_active",
					S_IFREG | S_IRUGO,
					votable->root, votable,
					&votable_force_ops);
	if (!votable->force_active_ent) {
		pr_err("Couldn't create force_active dbg file for %s\n", name);
		debugfs_remove_recursive(votable->root);
		kfree(votable->name);
		kfree(votable);
		return ERR_PTR(-EEXIST);
	}

	return votable;
}

void destroy_votable(struct votable *votable)
{
	unsigned long flags;
	int i;

	if (!votable)
		return;

	spin_lock_irqsave(&votable_list_slock, flags);
	list_del(&votable->list);
	spin_unlock_irqrestore(&votable_list_slock, flags);

	debugfs_remove_recursive(votable->root);

	for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++)
		kfree(votable->client_strs[i]);

	kfree(votable->name);
	kfree(votable);
}
+345 −239

File changed.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include "pmic-voter.h"
#include <linux/pmic-voter.h>
#include "smb138x-charger.h"

#define SMB_DEFAULT_FCC_UA 1000000
+50 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015 The Linux Foundation. All rights reserved.
/* Copyright (c) 2015-2017 The Linux Foundation. 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 version 2 and
@@ -24,22 +24,26 @@ enum votable_type {
	NUM_VOTABLE_TYPES,
};

int get_client_vote(struct votable *votable, int client_id);
int get_client_vote_locked(struct votable *votable, int client_id);
bool is_client_vote_enabled(struct votable *votable, const char *client_str);
bool is_client_vote_enabled_locked(struct votable *votable,
							const char *client_str);
int get_client_vote(struct votable *votable, const char *client_str);
int get_client_vote_locked(struct votable *votable, const char *client_str);
int get_effective_result(struct votable *votable);
int get_effective_result_locked(struct votable *votable);
int get_effective_client_id(struct votable *votable);
int get_effective_client_id_locked(struct votable *votable);
int vote(struct votable *votable, int client_id, bool state, int val);
struct votable *create_votable(struct device *dev, const char *name,
				int votable_type, int num_clients,
				int default_result,
				int (*callback)(struct device *dev,
const char *get_effective_client(struct votable *votable);
const char *get_effective_client_locked(struct votable *votable);
int vote(struct votable *votable, const char *client_str, bool state, int val);
int rerun_election(struct votable *votable);
struct votable *find_votable(const char *name);
struct votable *create_votable(const char *name,
				int votable_type,
				int (*callback)(struct votable *votable,
						void *data,
						int effective_result,
						int effective_client,
						int last_result,
						int last_client)
					);
						const char *effective_client),
				void *data);
void destroy_votable(struct votable *votable);
void lock_votable(struct votable *votable);
void unlock_votable(struct votable *votable);