/*
 * kernel/power/main.c - PM subsystem core functionality.
 *
 * Copyright (c) 2003 Patrick Mochel
 * Copyright (c) 2003 Open Source Development Lab
 * 
 * This file is release under the GPLv2
 *
 */

#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pm.h>



static int standby(void)
{
	return 0;
}

static int suspend(void)
{
	return 0;
}

static int hibernate(void)
{
	return 0;
}

#define decl_state(_name) \
	{ .name = __stringify(_name), .fn = _name }

struct pm_state {
	char * name;
	int (*fn)(void);
} pm_states[] = {
	decl_state(standby),
	decl_state(suspend),
	decl_state(hibernate),
	{ NULL },
};


static int enter_state(struct pm_state * state)
{
	return state->fn();
}



decl_subsys(power,NULL,NULL);


#define power_attr(_name) \
static struct subsys_attribute _name##_attr = {	\
	.attr	= {				\
		.name = __stringify(_name),	\
		.mode = 0644,			\
	},					\
	.show	= _name##_show,			\
	.store	= _name##_store,		\
}

/**
 *	state - control system power state.
 *
 *	show() returns what states are supported, which is hard-coded to
 *	'standby' (Power-On Suspend), 'suspend' (Suspend-to-RAM), and
 *	'hibernate' (Suspend-to-Disk).
 *
 *	store() accepts one of those strings, translates it into the 
 *	proper enumerated value, and initiates a suspend transition.
 */

static ssize_t state_show(struct subsystem * subsys, char * buf)
{
	struct pm_state * state;
	char * s = buf;

	for (state = &pm_states[0]; state->name; state++)
		s += sprintf(s,"%s ",state->name);
	s += sprintf(s,"\n");
	return (s - buf);
}

static ssize_t state_store(struct subsystem * s, const char * buf, size_t n)
{
	struct pm_state * state;
	int error;
	char * end = strchr(buf,'\n');
	
	if (end)
		*end = '\0';

	for (state = &pm_states[0]; state; state++) {
		if (!strcmp(buf,state->name))
			break;
	}
	if (!state)
		return -EINVAL;
	error = enter_state(state);
	return error ? error : n;
}

power_attr(state);

static struct attribute * g[] = {
	&state_attr.attr,
	NULL,
};

static struct attribute_group attr_group = {
	.attrs = g,
};


static int pm_init(void)
{
	int error = subsystem_register(&power_subsys);
	if (!error)
		error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
	return error;
}

core_initcall(pm_init);