/*
 * onetouch.c -- code to handling OneTouch buttons
 * 
 * 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, 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.
 *
 * Written by Soós Péter <sp@osb.hu>, 2002-2003
 */


#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/pm.h>
#include <linux/proc_fs.h>

#include <asm/system.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>

#ifdef OMNIBOOK_STANDALONE
#include "omnibook.h"
#else
#include <linux/omnibook.h>
#endif

#include "ec.h"

static struct pm_dev *pm_onetouch;
static pm_callback pm_onetouch_callback = NULL;

static struct proc_dir_entry *proc_onetouch;

/* There is no information about reading OneTouch status */
int omnibook_onetouch_enabled = 0;

static int omnibook_onetouch_on(void)
{
	if (omnibook_kbc_command(OMNIBOOK_KBC_CONTROL_CMD, OMNIBOOK_KBC_CMD_ONETOUCH_ENABLE)) {
		printk(KERN_ERR "%s: failed OneTouch enable command.\n", MODULE_NAME);
		return -EIO;
	}
	return 0;
}

static int omnibook_onetouch_off(void)
{
	if (omnibook_kbc_command(OMNIBOOK_KBC_CONTROL_CMD, OMNIBOOK_KBC_CMD_ONETOUCH_DISABLE)) {
		printk(KERN_ERR "%s: failed OneTouch disable command.\n", MODULE_NAME);
		return -EIO;
	}
	return 0;
}

/*
 * Power management handler: on resume it reenables the OneTouch buttons it they were enabled previously
 */

static int pm_onetouch_handler(struct pm_dev *dev, pm_request_t rqst, void *data)
{
	switch (rqst) {
	case PM_RESUME:
		if (omnibook_onetouch_enabled)
			return omnibook_onetouch_on();
		break;
	case PM_SUSPEND:
		if (omnibook_onetouch_enabled)
			return omnibook_onetouch_off();
		break;
	}
	return 0;
}

static int omnibook_onetouch_register(void)
{
		pm_onetouch_callback = pm_onetouch_handler;
		pm_onetouch = pm_register(PM_SYS_DEV, PM_SYS_KBC, pm_onetouch_callback);
		if (pm_onetouch)
			return 0;
		else
			return -EFAULT;
}

static void omnibook_onetouch_unregister(void)
{
		pm_onetouch_callback = NULL;
		pm_unregister(pm_onetouch);
}

int omnibook_onetouch_enable(void)
{
#ifdef CONFIG_VT
	switch (omnibook_ectype) {
	case XE3GC:
	case XE3GF:
	case OB500:
	case OB510:
	case OB6000:
	case OB6100:
	case XE4500:
		if (! omnibook_onetouch_enabled) {
			if (omnibook_onetouch_on())
				return -EIO;
			omnibook_onetouch_enabled = 1;
			printk(KERN_INFO "%s: OneTouch buttons are enabled.\n", MODULE_NAME);
		}
		break;
	default:
		omnibook_onetouch_enabled = 0;
		return -ENODEV;
	}
#endif
	return 0;
}

int omnibook_onetouch_disable(void)
{
#ifdef CONFIG_VT
	switch (omnibook_ectype) {
	case XE3GC:
	case XE3GF:
	case OB500:
	case OB510:
	case OB6000:
	case OB6100:
	case XE4500:
		if (omnibook_onetouch_enabled) {
			if (omnibook_onetouch_off()) {
				return -EIO;
			}
			omnibook_onetouch_enabled = 0;
			printk(KERN_INFO "%s: OneTouch buttons are disabled.\n", MODULE_NAME);
		}
		break;
	default:
		omnibook_onetouch_enabled = 0;
		return -ENODEV;
	}
#endif
	return 0;
}

static int omnibook_onetouch_status(char *buffer, char **start, off_t off, int count, int *eof, void *data)
{
	int len;
	char *b = buffer;
	char *str;

	str = (omnibook_onetouch_enabled) ? "enabled" : "disabled";
	
	b += sprintf(b, "OneTouch buttons are %s\n", str);

	len = b - buffer;
	if (len < off + count)
		*eof = 1;
	*start = buffer + off;
	len -= off;
	if (len > count)
		len = count;
	if (len < 0)
		len = 0;
	return len;
}

static int omnibook_onetouch_set(struct file *file, const char *buffer, unsigned long count, void *data)
{
	char status[1] = {'\0'};

	if (copy_from_user(status, buffer, 1))
		return -EFAULT;
	switch (*status) {
	case '0':
		omnibook_onetouch_disable();
		break;
	case '1':
		omnibook_onetouch_enable();
		break;
	default:
		count = -EINVAL;
	}
	return count;
}

int __init omnibook_onetouch_init(void)
{
#ifdef CONFIG_VT
	int retval;
	mode_t pmode;

	switch (omnibook_ectype) {
	case XE3GC:
	case XE3GF:
	case OB500:
	case OB510:
	case OB6000:
	case OB6100:
	case XE4500:
		pmode = S_IFREG | S_IWUSR | S_IRUGO;
		if (omnibook_userset)
			pmode = pmode | S_IWUGO;
		proc_onetouch = create_proc_entry("onetouch", pmode, omnibook_proc_root);
		break;
	default:
		printk(KERN_INFO "%s: OneTouch button handling is unsupported on this machine.\n", MODULE_NAME);
		omnibook_onetouch_enabled = 0;
		return 0;
	}
	if (proc_onetouch) {
		proc_onetouch->read_proc = omnibook_onetouch_status;
		proc_onetouch->write_proc = omnibook_onetouch_set;
	} else {
		printk(KERN_ERR "%s: Unable to create /proc/%s/onetouch.\n", MODULE_NAME, MODULE_NAME);
		return -ENOENT;
	}
	retval = omnibook_onetouch_register();
	if (retval)
		return retval;
	retval = omnibook_onetouch_enable();
	if (retval)
		return retval;

	return 0;
#else
	printk(KERN_ERR "%s: Virtual terminal support is not compiled into your kernel.\n", MODULE_NAME);
	return -ENODEV;
#endif
}

void __exit omnibook_onetouch_cleanup(void)
{
#ifdef CONFIG_VT
	if (proc_onetouch)
		remove_proc_entry("onetouch", omnibook_proc_root);
	if (pm_onetouch)
		omnibook_onetouch_unregister();
	
	omnibook_onetouch_disable();
#endif
}

EXPORT_SYMBOL(omnibook_onetouch_enabled);
EXPORT_SYMBOL(omnibook_onetouch_enable);
EXPORT_SYMBOL(omnibook_onetouch_disable);

/* End of file */
