/*
 * blank.c -- code for blanking lcd console
 * 
 * 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 proc_dir_entry *proc_blank;
int omnibook_console_blank_enabled = 0;

extern int (*console_blank_hook)(int);

/*
 * Turn LCD display off at console blanking if APM does not do it
 * (e.g. using ACPI).
 */

int omnibook_lcd_blank(int blank)
{
	int retval = 0;
#ifdef CONFIG_VT
	u8 cmd;
	
	switch (omnibook_ectype) {
	case XE3GF:
	case XE3GC:
		cmd = blank ? OMNIBOOK_KBC_CMD_LCD_OFF : OMNIBOOK_KBC_CMD_LCD_ON;
		retval = omnibook_kbc_command(OMNIBOOK_KBC_CONTROL_CMD, cmd);
		if (retval)
			return retval;
		break;
	case OB500:
	case OB6000:
		retval = omnibook_io_read(OB500_GPO1, &cmd);
		if (retval)
			return retval;
		cmd = blank ? cmd & ~OB500_BKLT_MASK : cmd | OB500_BKLT_MASK;
		retval = omnibook_io_write(OB500_GPO1, cmd);
		if (retval)
			return retval;
		break;
	case OB510:
	case OB6100:
		retval = omnibook_io_read(OB510_GPO2, &cmd);
		if (retval)
			return retval;
		cmd = blank ? cmd & ~OB510_BKLT_MASK : cmd | OB510_BKLT_MASK;
		retval = omnibook_io_write(OB510_GPO2, cmd);
		if (retval)
			return retval;
		break;
	default:
		printk(KERN_INFO "%s: LCD console blanking is unsupported on this machine.\n", MODULE_NAME);
		retval = -ENODEV;
	}
#endif
	return retval;
}

int omnibook_console_blank_enable(void)
{
#ifdef CONFIG_VT
	if (omnibook_console_blank_enabled == 0) {
		if (console_blank_hook == NULL) {
			console_blank_hook = omnibook_lcd_blank;
			printk(KERN_INFO "%s: LCD display turn off at console blanking enabled.\n", MODULE_NAME);
			omnibook_console_blank_enabled = 1;
		} else {
			printk(KERN_INFO "%s: There is a console blanking solution already registered.\n", MODULE_NAME);
		}
	}
#endif
	return 0;
}
	
int omnibook_console_blank_disable(void)
{
#ifdef CONFIG_VT
	if (console_blank_hook == omnibook_lcd_blank) {
		console_blank_hook = NULL;
		printk(KERN_INFO "%s: LCD display turn off at console blanking disabled.\n", MODULE_NAME);
		omnibook_console_blank_enabled = 0;
	} else if (console_blank_hook) {
		printk(KERN_WARNING "%s: You can not disable another console blanking solution.\n", MODULE_NAME);
		return -EBUSY;
	} else {
		printk(KERN_INFO "%s: Console blanking already disabled.\n", MODULE_NAME);
		return 0;
	}
#endif
	return 0;
}

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

	str = (omnibook_console_blank_enabled) ? "enabled" : "disabled";

	b += sprintf(b, "LCD console blanking is %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_console_blank_set(struct file *file, const char *buffer, unsigned long count, void *data)
{
	char status[1] = {'\0'};
	int retval;

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

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

	switch (omnibook_ectype) {
	case XE3GF:
	case XE3GC:
	case OB500:
	case OB510:
	case OB6000:
	case OB6100:
		retval = omnibook_console_blank_enable();
		if (retval)
			return retval;
		pmode = S_IFREG | S_IWUSR | S_IRUGO;
		if (omnibook_userset)
			pmode = pmode | S_IWUGO;
		proc_blank = create_proc_entry("blank", pmode, omnibook_proc_root);
		break;
	default:
		printk(KERN_INFO "%s: LCD console blanking is unsupported on this machine.\n", MODULE_NAME);
		return 0;
	}
	
	if (proc_blank) {
		proc_blank->read_proc = omnibook_console_blank_status;
		proc_blank->write_proc = omnibook_console_blank_set;
	} else {
		printk(KERN_ERR "%s: Unable to create /proc/%s/blank.\n", MODULE_NAME, MODULE_NAME);
		return -ENOENT;
	}
	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_console_blank_cleanup(void)
{
#ifdef CONFIG_VT
	if (proc_blank) {
		remove_proc_entry("blank", omnibook_proc_root);
		omnibook_console_blank_disable();
	}
#endif
}

EXPORT_SYMBOL(omnibook_console_blank_enabled);
EXPORT_SYMBOL(omnibook_lcd_blank);
EXPORT_SYMBOL(omnibook_console_blank_enable);
EXPORT_SYMBOL(omnibook_console_blank_disable);

/* End of file */
