diff options
| author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 | 
| commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
| tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/acpi/button.c | |
| download | olio-linux-3.10-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.xz olio-linux-3.10-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.zip  | |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/acpi/button.c')
| -rw-r--r-- | drivers/acpi/button.c | 558 | 
1 files changed, 558 insertions, 0 deletions
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c new file mode 100644 index 00000000000..ec4430e3053 --- /dev/null +++ b/drivers/acpi/button.c @@ -0,0 +1,558 @@ +/* + *  acpi_button.c - ACPI Button Driver ($Revision: 30 $) + * + *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + *  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 of the License, 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. + * + *  You should have received a copy of the GNU General Public License along + *  with this program; if not, write to the Free Software Foundation, Inc., + *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#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> + + +#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" +#define ACPI_BUTTON_HID_POWER		"PNP0C0C"	 +#define ACPI_BUTTON_DEVICE_NAME_POWER	"Power Button (CM)" +#define ACPI_BUTTON_DEVICE_NAME_POWERF	"Power Button (FF)" +#define ACPI_BUTTON_TYPE_POWER		0x01 +#define ACPI_BUTTON_TYPE_POWERF		0x02 + +#define ACPI_BUTTON_SUBCLASS_SLEEP	"sleep" +#define ACPI_BUTTON_HID_SLEEP		"PNP0C0E" +#define ACPI_BUTTON_DEVICE_NAME_SLEEP	"Sleep Button (CM)" +#define ACPI_BUTTON_DEVICE_NAME_SLEEPF	"Sleep Button (FF)" +#define ACPI_BUTTON_TYPE_SLEEP		0x03 +#define ACPI_BUTTON_TYPE_SLEEPF		0x04 + +#define ACPI_BUTTON_SUBCLASS_LID	"lid" +#define ACPI_BUTTON_HID_LID		"PNP0C0D" +#define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch" +#define ACPI_BUTTON_TYPE_LID		0x05 + +#define _COMPONENT		ACPI_BUTTON_COMPONENT +ACPI_MODULE_NAME		("acpi_button") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME); +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, +	.class =	ACPI_BUTTON_CLASS, +	.ids =		"ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E", +	.ops =		{ +				.add =		acpi_button_add, +				.remove =	acpi_button_remove, +			}, +}; + +struct acpi_button { +	acpi_handle		handle; +	struct acpi_device	*device;	/* Fixed button kludge */ +	u8			type; +	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 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: +			entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,  +				acpi_button_dir); +		break; +	case ACPI_BUTTON_TYPE_SLEEP: +	case ACPI_BUTTON_TYPE_SLEEPF: +			entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,  +				acpi_button_dir); +		break; +	case ACPI_BUTTON_TYPE_LID: +			entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,  +				acpi_button_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); + + +		switch (button->type) { +			case ACPI_BUTTON_TYPE_POWER: +			case ACPI_BUTTON_TYPE_POWERF: +				remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER,  +					acpi_button_dir); +				break; +			case ACPI_BUTTON_TYPE_SLEEP: +			case ACPI_BUTTON_TYPE_SLEEPF: +				remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP,  +					acpi_button_dir); +				break; +			case ACPI_BUTTON_TYPE_LID: +				remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID,  +					acpi_button_dir); +				break; +		} +		acpi_device_dir(device) = NULL; +	} + +	return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- +                                Driver Interface +   -------------------------------------------------------------------------- */ + +static void +acpi_button_notify ( +	acpi_handle		handle, +	u32			event, +	void			*data) +{ +	struct acpi_button	*button = (struct acpi_button *) data; + +	ACPI_FUNCTION_TRACE("acpi_button_notify"); + +	if (!button || !button->device) +		return_VOID; + +	switch (event) { +	case ACPI_BUTTON_NOTIFY_STATUS: +		acpi_bus_generate_event(button->device, event, ++button->pushed); +		break; +	default: +		ACPI_DEBUG_PRINT((ACPI_DB_INFO, +			"Unsupported event [0x%x]\n", event)); +		break; +	} + +	return_VOID; +} + + +static acpi_status +acpi_button_notify_fixed ( +	void			*data) +{ +	struct acpi_button	*button = (struct acpi_button *) data; +	 +	ACPI_FUNCTION_TRACE("acpi_button_notify_fixed"); + +	if (!button) +		return_ACPI_STATUS(AE_BAD_PARAMETER); + +	acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button); + +	return_ACPI_STATUS(AE_OK); +} + + +static int +acpi_button_add ( +	struct acpi_device	*device) +{ +	int			result = 0; +	acpi_status		status = AE_OK; +	struct acpi_button	*button = NULL; + +	static struct acpi_device *power_button; +	static struct acpi_device *sleep_button; +	static struct acpi_device *lid_button; + +	ACPI_FUNCTION_TRACE("acpi_button_add"); + +	if (!device) +		return_VALUE(-EINVAL); + +	button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL); +	if (!button) +		return_VALUE(-ENOMEM); +	memset(button, 0, sizeof(struct acpi_button)); + +	button->device = device; +	button->handle = device->handle; +	acpi_driver_data(device) = button; + +	/* +	 * Determine the button type (via hid), as fixed-feature buttons +	 * need to be handled a bit differently than generic-space. +	 */ +	if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) { +		button->type = ACPI_BUTTON_TYPE_POWER; +		strcpy(acpi_device_name(device), +			ACPI_BUTTON_DEVICE_NAME_POWER); +		sprintf(acpi_device_class(device), "%s/%s",  +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); +	} +	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) { +		button->type = ACPI_BUTTON_TYPE_POWERF; +		strcpy(acpi_device_name(device), +			ACPI_BUTTON_DEVICE_NAME_POWERF); +		sprintf(acpi_device_class(device), "%s/%s",  +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); +	} +	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) { +		button->type = ACPI_BUTTON_TYPE_SLEEP; +		strcpy(acpi_device_name(device), +			ACPI_BUTTON_DEVICE_NAME_SLEEP); +		sprintf(acpi_device_class(device), "%s/%s",  +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); +	} +	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) { +		button->type = ACPI_BUTTON_TYPE_SLEEPF; +		strcpy(acpi_device_name(device), +			ACPI_BUTTON_DEVICE_NAME_SLEEPF); +		sprintf(acpi_device_class(device), "%s/%s",  +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); +	} +	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) { +		button->type = ACPI_BUTTON_TYPE_LID; +		strcpy(acpi_device_name(device), +			ACPI_BUTTON_DEVICE_NAME_LID); +		sprintf(acpi_device_class(device), "%s/%s",  +			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); +	} +	else { +		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n", +			acpi_device_hid(device))); +		result = -ENODEV; +		goto end; +	} + +	/* +	 * Ensure only one button of each type is used. +	 */ +	switch (button->type) { +	case ACPI_BUTTON_TYPE_POWER: +	case ACPI_BUTTON_TYPE_POWERF: +		if (!power_button) +			power_button = device; +		else { +			kfree(button); +			return_VALUE(-ENODEV); +		} +		break; +	case ACPI_BUTTON_TYPE_SLEEP: +	case ACPI_BUTTON_TYPE_SLEEPF: +		if (!sleep_button) +			sleep_button = device; +		else { +			kfree(button); +			return_VALUE(-ENODEV); +		} +		break; +	case ACPI_BUTTON_TYPE_LID: +		if (!lid_button) +			lid_button = device; +		else { +			kfree(button); +			return_VALUE(-ENODEV); +		} +		break; +	} + +	result = acpi_button_add_fs(device); +	if (result) +		goto end; + +	switch (button->type) { +	case ACPI_BUTTON_TYPE_POWERF: +		status = acpi_install_fixed_event_handler ( +			ACPI_EVENT_POWER_BUTTON, +			acpi_button_notify_fixed, +			button); +		break; +	case ACPI_BUTTON_TYPE_SLEEPF: +		status = acpi_install_fixed_event_handler ( +			ACPI_EVENT_SLEEP_BUTTON, +			acpi_button_notify_fixed, +			button); +		break; +	default: +		status = acpi_install_notify_handler ( +			button->handle, +			ACPI_DEVICE_NOTIFY, +			acpi_button_notify, +			button); +		break; +	} + +	if (ACPI_FAILURE(status)) { +		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, +			"Error installing notify handler\n")); +		result = -ENODEV; +		goto end; +	} + +	if (device->wakeup.flags.valid) { +		/* Button's GPE is run-wake GPE */ +		acpi_set_gpe_type(device->wakeup.gpe_device,  +			device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); +		acpi_enable_gpe(device->wakeup.gpe_device,  +			device->wakeup.gpe_number, ACPI_NOT_ISR); +		device->wakeup.state.enabled = 1; +	} + +	printk(KERN_INFO PREFIX "%s [%s]\n",  +		acpi_device_name(device), acpi_device_bid(device)); + +end: +	if (result) { +		acpi_button_remove_fs(device); +		kfree(button); +	} + +	return_VALUE(result); +} + + +static int +acpi_button_remove (struct acpi_device *device, int type) +{ +	acpi_status		status = 0; +	struct acpi_button	*button = NULL; + +	ACPI_FUNCTION_TRACE("acpi_button_remove"); + +	if (!device || !acpi_driver_data(device)) +		return_VALUE(-EINVAL); + +	button = acpi_driver_data(device); + +	/* Unregister for device notifications. */ +	switch (button->type) { +	case ACPI_BUTTON_TYPE_POWERF: +		status = acpi_remove_fixed_event_handler( +			ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed); +		break; +	case ACPI_BUTTON_TYPE_SLEEPF: +		status = acpi_remove_fixed_event_handler( +			ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed); +		break; +	default: +		status = acpi_remove_notify_handler(button->handle, +			ACPI_DEVICE_NOTIFY, acpi_button_notify); +		break; +	} + +	if (ACPI_FAILURE(status)) +		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, +			"Error removing notify handler\n")); + +	acpi_button_remove_fs(device);	 + +	kfree(button); + +	return_VALUE(0); +} + + +static int __init +acpi_button_init (void) +{ +	int			result = 0; + +	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) +{ +	ACPI_FUNCTION_TRACE("acpi_button_exit"); + +	acpi_bus_unregister_driver(&acpi_button_driver); + +	remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); + +	return_VOID; +} + + +module_init(acpi_button_init); +module_exit(acpi_button_exit);  |