[ACPI] restore /proc/acpi/button/ (ala 2.6.12)

Signed-off-by Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
Signed-off-by Len Brown <len.brown@intel.com>
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 0f45d45..8162fd0 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -26,6 +26,9 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/types.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 
@@ -33,6 +36,9 @@
 #define ACPI_BUTTON_COMPONENT		0x00080000
 #define ACPI_BUTTON_DRIVER_NAME		"ACPI Button Driver"
 #define ACPI_BUTTON_CLASS		"button"
+#define ACPI_BUTTON_FILE_INFO		"info"
+#define ACPI_BUTTON_FILE_STATE		"state"
+#define ACPI_BUTTON_TYPE_UNKNOWN	0x00
 #define ACPI_BUTTON_NOTIFY_STATUS	0x80
 
 #define ACPI_BUTTON_SUBCLASS_POWER	"power"
@@ -64,6 +70,8 @@
 
 static int acpi_button_add (struct acpi_device *device);
 static int acpi_button_remove (struct acpi_device *device, int type);
+static int acpi_button_info_open_fs(struct inode *inode, struct file *file);
+static int acpi_button_state_open_fs(struct inode *inode, struct file *file);
 
 static struct acpi_driver acpi_button_driver = {
 	.name =		ACPI_BUTTON_DRIVER_NAME,
@@ -82,6 +90,179 @@
 	unsigned long		pushed;
 };
 
+static struct file_operations acpi_button_info_fops = {
+	.open		= acpi_button_info_open_fs,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static struct file_operations acpi_button_state_fops = {
+	.open		= acpi_button_state_open_fs,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+/* --------------------------------------------------------------------------
+                              FS Interface (/proc)
+   -------------------------------------------------------------------------- */
+
+static struct proc_dir_entry	*acpi_button_dir;
+
+static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_button	*button = (struct acpi_button *) seq->private;
+
+	ACPI_FUNCTION_TRACE("acpi_button_info_seq_show");
+
+	if (!button || !button->device)
+		return_VALUE(0);
+
+	seq_printf(seq, "type:                    %s\n", 
+		acpi_device_name(button->device));
+
+	return_VALUE(0);
+}
+
+static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
+{
+	return single_open(file, acpi_button_info_seq_show, PDE(inode)->data);
+}
+	
+static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
+{
+	struct acpi_button	*button = (struct acpi_button *) seq->private;
+	acpi_status		status;
+	unsigned long		state;
+
+	ACPI_FUNCTION_TRACE("acpi_button_state_seq_show");
+
+	if (!button || !button->device)
+		return_VALUE(0);
+
+	status = acpi_evaluate_integer(button->handle,"_LID",NULL,&state);
+	if (ACPI_FAILURE(status)) {
+		seq_printf(seq, "state:      unsupported\n");
+	}
+	else{
+		seq_printf(seq, "state:      %s\n", (state ? "open" : "closed")); 
+	}
+
+	return_VALUE(0);
+}
+
+static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
+{
+	return single_open(file, acpi_button_state_seq_show, PDE(inode)->data);
+}
+
+static struct proc_dir_entry *acpi_power_dir;
+static struct proc_dir_entry *acpi_sleep_dir;
+static struct proc_dir_entry *acpi_lid_dir;
+
+static int
+acpi_button_add_fs (
+	struct acpi_device	*device)
+{
+	struct proc_dir_entry	*entry = NULL;
+	struct acpi_button	*button = NULL;
+
+	ACPI_FUNCTION_TRACE("acpi_button_add_fs");
+
+	if (!device || !acpi_driver_data(device))
+		return_VALUE(-EINVAL);
+
+	button = acpi_driver_data(device);
+
+	switch (button->type) {
+	case ACPI_BUTTON_TYPE_POWER:
+	case ACPI_BUTTON_TYPE_POWERF:
+		if (!acpi_power_dir)
+			acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER, 
+				acpi_button_dir);
+		entry = acpi_power_dir;
+		break;
+	case ACPI_BUTTON_TYPE_SLEEP:
+	case ACPI_BUTTON_TYPE_SLEEPF:
+		if (!acpi_sleep_dir)
+			acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP, 
+				acpi_button_dir);
+		entry = acpi_sleep_dir;
+		break;
+	case ACPI_BUTTON_TYPE_LID:
+		if (!acpi_lid_dir)
+			acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, 
+				acpi_button_dir);
+		entry = acpi_lid_dir;
+		break;
+	}
+
+	if (!entry)
+		return_VALUE(-ENODEV);
+	entry->owner = THIS_MODULE;
+
+	acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
+	if (!acpi_device_dir(device))
+		return_VALUE(-ENODEV);
+	acpi_device_dir(device)->owner = THIS_MODULE;
+
+	/* 'info' [R] */
+	entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
+		S_IRUGO, acpi_device_dir(device));
+	if (!entry)
+		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+			"Unable to create '%s' fs entry\n",
+			ACPI_BUTTON_FILE_INFO));
+	else {
+		entry->proc_fops = &acpi_button_info_fops;
+		entry->data = acpi_driver_data(device);
+		entry->owner = THIS_MODULE;
+	}
+
+	/* show lid state [R] */
+	if (button->type == ACPI_BUTTON_TYPE_LID) {
+		entry = create_proc_entry(ACPI_BUTTON_FILE_STATE,
+			S_IRUGO, acpi_device_dir(device));
+		if (!entry)
+			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+				"Unable to create '%s' fs entry\n",
+				ACPI_BUTTON_FILE_INFO));
+		else {
+			entry->proc_fops = &acpi_button_state_fops;
+			entry->data = acpi_driver_data(device);
+			entry->owner = THIS_MODULE;
+		}
+	}
+
+	return_VALUE(0);
+}
+
+
+static int
+acpi_button_remove_fs (
+	struct acpi_device	*device)
+{
+	struct acpi_button	*button = NULL;
+
+	ACPI_FUNCTION_TRACE("acpi_button_remove_fs");
+
+	button = acpi_driver_data(device);
+	if (acpi_device_dir(device)) {
+		if (button->type == ACPI_BUTTON_TYPE_LID)
+			remove_proc_entry(ACPI_BUTTON_FILE_STATE,
+					     acpi_device_dir(device));
+		remove_proc_entry(ACPI_BUTTON_FILE_INFO,
+				     acpi_device_dir(device));
+
+		remove_proc_entry(acpi_device_bid(device),
+				     acpi_device_dir(device)->parent);
+		acpi_device_dir(device) = NULL;
+	}
+
+	return_VALUE(0);
+}
+
+
 /* --------------------------------------------------------------------------
                                 Driver Interface
    -------------------------------------------------------------------------- */
@@ -121,7 +302,8 @@
 	
 	ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
 
-	BUG_ON(!button);
+	if (!button)
+		return_ACPI_STATUS(AE_BAD_PARAMETER);
 
 	acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
 
@@ -197,6 +379,10 @@
 		goto end;
 	}
 
+	result = acpi_button_add_fs(device);
+	if (result)
+		goto end;
+
 	switch (button->type) {
 	case ACPI_BUTTON_TYPE_POWERF:
 		status = acpi_install_fixed_event_handler (
@@ -240,6 +426,7 @@
 
 end:
 	if (result) {
+		acpi_button_remove_fs(device);
 		kfree(button);
 	}
 
@@ -280,6 +467,8 @@
 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
 			"Error removing notify handler\n"));
 
+	acpi_button_remove_fs(device);	
+
 	kfree(button);
 
 	return_VALUE(0);
@@ -293,14 +482,20 @@
 
 	ACPI_FUNCTION_TRACE("acpi_button_init");
 
+	acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
+	if (!acpi_button_dir)
+		return_VALUE(-ENODEV);
+	acpi_button_dir->owner = THIS_MODULE;
 	result = acpi_bus_register_driver(&acpi_button_driver);
 	if (result < 0) {
+		remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
 		return_VALUE(-ENODEV);
 	}
 
 	return_VALUE(0);
 }
 
+
 static void __exit
 acpi_button_exit (void)
 {
@@ -308,8 +503,17 @@
 
 	acpi_bus_unregister_driver(&acpi_button_driver);
 
+	if (acpi_power_dir) 
+		remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir);
+	if (acpi_sleep_dir)
+		remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir);
+	if (acpi_lid_dir)
+		remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
+	remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
+
 	return_VOID;
 }
 
+
 module_init(acpi_button_init);
 module_exit(acpi_button_exit);