diff options
Diffstat (limited to 'drivers/acpi/button.c')
-rw-r--r-- | drivers/acpi/button.c | 206 |
1 files changed, 205 insertions, 1 deletions
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 0f45d45f05a0..8162fd0c21a7 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 @@ MODULE_LICENSE("GPL"); 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 @@ struct acpi_button { 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_button_notify_fixed ( 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 @@ acpi_button_add ( 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 @@ acpi_button_add ( end: if (result) { + acpi_button_remove_fs(device); kfree(button); } @@ -280,6 +467,8 @@ acpi_button_remove (struct acpi_device *device, int type) 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_button_init (void) 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_button_exit (void) 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); |